From b593e75c6741b4fdee9e579294309414619e36fe Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Fri, 23 May 2025 02:17:23 -0500 Subject: [PATCH 01/41] build main structure and build data layer and space managment bloc --- lib/core/network/custom_exceptions.dart | 16 +++ lib/core/network/dio.dart | 36 ++++++ lib/core/network/end_points.dart | 3 + lib/core/network/enums.dart | 50 ++++++++ lib/core/network/request.dart | 121 ++++++++++++++++++ .../communities_bloc/communities_bloc.dart | 37 ++++++ .../communities_bloc/communities_event.dart | 10 ++ .../communities_bloc/communities_state.dart | 30 +++++ .../refactor/data/models/community_model.dart | 21 +++ .../refactor/data/models/device_model.dart | 20 +++ .../refactor/data/models/space_model.dart | 27 ++++ .../refactor/data/models/sub_space_model.dart | 19 +++ .../space_managment_remote_source.dart | 17 +++ .../screens/space_mamagment_screen.dart | 0 14 files changed, 407 insertions(+) create mode 100644 lib/core/network/custom_exceptions.dart create mode 100644 lib/core/network/dio.dart create mode 100644 lib/core/network/end_points.dart create mode 100644 lib/core/network/enums.dart create mode 100644 lib/core/network/request.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart create mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/community_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/device_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/space_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/models/sub_space_model.dart create mode 100644 lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart create mode 100644 lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart diff --git a/lib/core/network/custom_exceptions.dart b/lib/core/network/custom_exceptions.dart new file mode 100644 index 00000000..23b81a8d --- /dev/null +++ b/lib/core/network/custom_exceptions.dart @@ -0,0 +1,16 @@ + +import 'enums.dart'; + +class GenericException implements Exception { + final ExceptionType type; + final String errorMessage; + const GenericException( + {required this.type, this.errorMessage = "Unknown Error"}); + + @override + String toString() { + return errorMessage; + } + + List get props => [type, errorMessage]; +} diff --git a/lib/core/network/dio.dart b/lib/core/network/dio.dart new file mode 100644 index 00000000..e01fef90 --- /dev/null +++ b/lib/core/network/dio.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; + +import '../../utils/constants/api_const.dart'; + +class DioInstance { + Dio? _dio; + + Dio get dio => _dio ?? _instantiate(); + + String? baseUrl; + + DioInstance({this.baseUrl}); + + Dio _instantiate() { + Dio dio = Dio( + BaseOptions( + baseUrl: baseUrl ?? '${ApiEndpoints.baseUrl}/', + receiveDataWhenStatusError: true, + headers: { + 'content_Type': 'application/json', + }, + ), + ); + + dio.interceptors.add( + LogInterceptor( + responseHeader: false, + requestHeader: false, + requestBody: true, + responseBody: true, + ), + ); + + return dio; + } +} diff --git a/lib/core/network/end_points.dart b/lib/core/network/end_points.dart new file mode 100644 index 00000000..3e2afd84 --- /dev/null +++ b/lib/core/network/end_points.dart @@ -0,0 +1,3 @@ +class EndPoints { + static const String fetchCommunities = 'projects/{projectUuid}/communities'; +} diff --git a/lib/core/network/enums.dart b/lib/core/network/enums.dart new file mode 100644 index 00000000..488d7cb9 --- /dev/null +++ b/lib/core/network/enums.dart @@ -0,0 +1,50 @@ +enum RequestType { + get, + post, + delete, + put, +} + +enum ExceptionType { + notAuthenticated, + connectionError, + // related to http status code exceptions + notAuthorized, + notFound, + internalServerException, + serviceUnavailableException, + pageGone, + + // related to bad request status code + // related to auth requests + invalidCredentials, + solutionAlreadySunmitted, + invalidValidation, + // other + other, +} + +enum ExceptionMessage { + NOT_AUTHENTICATED, + INVALID_CREDENTIALS, + The_password_field_must_be_at_least_8_characters, + SOLUTION_ALREADY_SUBMITTED, + you_are_not_authorized, + page_not_found, + page_gone, + INTERNAL_SERVER_ERROR, + service_unavailable, +} + +enum NotificationsType { + payment, + transporation, + product, + zero, +} + +enum ImagesType { + assets, + svg, + network, +} diff --git a/lib/core/network/request.dart b/lib/core/network/request.dart new file mode 100644 index 00000000..c242b8a6 --- /dev/null +++ b/lib/core/network/request.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; + +import 'package:dio/dio.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +import '../../pages/auth/model/token.dart'; +import 'custom_exceptions.dart'; +import 'dio.dart'; +import 'enums.dart'; + +class Request { + String endPoint; + bool? autherized; + bool? isFormData; + RequestType? method; + Map? headers; + final Map? queryParams; + Map? body; + Duration? receiveTimeout; + Request( + this.endPoint, { + this.autherized, + this.isFormData, + this.method, + this.headers, + this.queryParams, + this.body, + this.receiveTimeout, + }) { + headers = { + 'content_Type': 'application/json', + 'Accept': 'application/json', + }; + } + Future> sendRequest() async { + Response? response; + if (autherized != null && autherized!) { + final storage = const FlutterSecureStorage(); + final token = await storage.read(key: Token.loginAccessTokenKey); + if (token != null) { + headers!["authorization"] = "Bearer $token"; + } + } + try { + response = await DioInstance().dio.request( + endPoint, + queryParameters: queryParams, + data: isFormData != null && isFormData == true + ? FormData.fromMap(body!) + : body, + options: Options( + method: method!.name.toUpperCase(), + headers: headers, + contentType: 'application/json', + receiveTimeout: receiveTimeout, + ), + ); + if (response.statusCode! >= 200 && response.statusCode! < 300) { + return response.data; + } + } on DioException catch (error) { + if (error.type == DioExceptionType.badResponse) { + throw badRequestException[error.response!.data["error"]] ?? + const GenericException( + type: ExceptionType.other, + ); + } + if (error.type == DioExceptionType.connectionError || + error.type == DioExceptionType.connectionTimeout || + error.type == DioExceptionType.receiveTimeout || + error.type == DioExceptionType.sendTimeout || + error.type == DioExceptionType.unknown) { + throw const GenericException( + type: ExceptionType.connectionError, + errorMessage: 'no_internet_connection', + ); + } + } + return {}; + } + + Map toJson() => { + 'endPoint': endPoint, + 'method': method!.name.toUpperCase(), + 'body': json.encode(body), + 'headers': headers, + 'queryParams': queryParams, + 'autherized': autherized, + 'isFormData': isFormData, + }; + @override + String toString() { + return jsonEncode(toJson()); + } + + List get props => [ + endPoint, + autherized, + isFormData, + method, + headers, + queryParams, + body, + receiveTimeout, + ]; +} + +Map badRequestException = { + ExceptionMessage.INVALID_CREDENTIALS.name: const GenericException( + type: ExceptionType.invalidCredentials, + errorMessage: "Invalid credentials ...", + ), + "The password field must be at least 8 characters.": const GenericException( + type: ExceptionType.invalidValidation, + errorMessage: 'password must be 8 or more characters', + ), + ExceptionMessage.NOT_AUTHENTICATED.name: const GenericException( + type: ExceptionType.notAuthenticated, + errorMessage: "not authenticated", + ), +}; diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart new file mode 100644 index 00000000..286318d5 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart @@ -0,0 +1,37 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; + +import '../../data/models/community_model.dart'; +import '../../data/sources/space_managment_remote_source.dart'; + +part 'communities_event.dart'; +part 'communities_state.dart'; + +class CommunitiesBloc extends Bloc { + SpaceManagementRemoteSource spaceManagementRemoteSource = + SpaceManagementRemoteSource(); + CommunitiesBloc() : super(CommunitiesInitial()) { + on((event, emit) { + if (event is FetchCommunitiesEvent) { + _fetchCommunities(emit); + } + }); + } + + Future _fetchCommunities(Emitter emit) async { + String? projectUuid = await ProjectManager.getProjectUUID(); + if (projectUuid == null) { + emit(const CommunitiesError("Project UUID is null")); + return; + } + emit(CommunitiesLoading()); + try { + final communities = + await spaceManagementRemoteSource.fetchCommunities(projectUuid); + emit(CommunitiesLoaded(communities)); + } catch (e) { + emit(CommunitiesError(e.toString())); + } + } +} diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart new file mode 100644 index 00000000..1d47af23 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart @@ -0,0 +1,10 @@ +part of 'communities_bloc.dart'; + +sealed class CommunitiesEvent extends Equatable { + const CommunitiesEvent(); + + @override + List get props => []; +} + +class FetchCommunitiesEvent extends CommunitiesEvent {} \ No newline at end of file diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart new file mode 100644 index 00000000..747d8d82 --- /dev/null +++ b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart @@ -0,0 +1,30 @@ +part of 'communities_bloc.dart'; + +sealed class CommunitiesState extends Equatable { + const CommunitiesState(); + + @override + List get props => []; +} + +final class CommunitiesInitial extends CommunitiesState {} + +final class CommunitiesLoading extends CommunitiesState {} + +final class CommunitiesLoaded extends CommunitiesState { + final List communities; + + const CommunitiesLoaded(this.communities); + + @override + List get props => [communities]; +} + +final class CommunitiesError extends CommunitiesState { + final String message; + + const CommunitiesError(this.message); + + @override + List get props => [message]; +} diff --git a/lib/pages/spaces_management/refactor/data/models/community_model.dart b/lib/pages/spaces_management/refactor/data/models/community_model.dart new file mode 100644 index 00000000..88c62ad0 --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/community_model.dart @@ -0,0 +1,21 @@ +import 'package:syncrow_web/pages/spaces_management/refactor/data/models/space_model.dart'; + +class CommunityModel { + String id, name; + List? spaces; + CommunityModel({ + required this.id, + required this.name, + this.spaces, + }); + + factory CommunityModel.fromJson(Map json) => CommunityModel( + id: json['id'], + name: json['name'], + spaces: SpaceModel.fromJsonList(json['spaces']), + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => CommunityModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/device_model.dart b/lib/pages/spaces_management/refactor/data/models/device_model.dart new file mode 100644 index 00000000..1d000a37 --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/device_model.dart @@ -0,0 +1,20 @@ +class DeviceModel { + String id, name, tag; + String location; + DeviceModel({ + required this.id, + required this.name, + required this.tag, + required this.location, + }); + factory DeviceModel.fromJson(Map json) => DeviceModel( + id: json['id'], + name: json['name'], + tag: json['tag'], + location: json['location'], + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => DeviceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/space_model.dart b/lib/pages/spaces_management/refactor/data/models/space_model.dart new file mode 100644 index 00000000..c2b6d73c --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/space_model.dart @@ -0,0 +1,27 @@ +import 'device_model.dart'; + +class SpaceModel { + String id, parentId, name; + List? spaces; + List? devices; + + SpaceModel({ + required this.id, + required this.parentId, + required this.name, + this.spaces, + this.devices, + }); + + factory SpaceModel.fromJson(Map json) => SpaceModel( + id: json['id'], + parentId: json['parentId'], + name: json['name'], + spaces: SpaceModel.fromJsonList(json['spaces']), + devices: DeviceModel.fromJsonList(json['devices']), + ); + + static List fromJsonList(List jsonList) { + return jsonList.map((json) => SpaceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart b/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart new file mode 100644 index 00000000..2893e0ed --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart @@ -0,0 +1,19 @@ +import 'device_model.dart'; + +class SubSpaceModel { + String id, name; + List? devices; + SubSpaceModel({ + required this.id, + required this.name, + this.devices, + }); + factory SubSpaceModel.fromJson(Map json) => SubSpaceModel( + id: json['id'], + name: json['name'], + devices: DeviceModel.fromJsonList(json['devices']), + ); + static List fromJsonList(List jsonList) { + return jsonList.map((json) => SubSpaceModel.fromJson(json)).toList(); + } +} diff --git a/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart b/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart new file mode 100644 index 00000000..189cabbf --- /dev/null +++ b/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart @@ -0,0 +1,17 @@ +import 'package:syncrow_web/core/network/end_points.dart'; +import 'package:syncrow_web/core/network/enums.dart'; +import 'package:syncrow_web/core/network/request.dart'; + +import '../models/community_model.dart'; + +class SpaceManagementRemoteSource { + Future> fetchCommunities(String projectUuid) async { + Request request = Request( + EndPoints.fetchCommunities.replaceFirst('{projectUuid}', projectUuid), + method: RequestType.get, + autherized: true, + ); + final response = await request.sendRequest(); + return CommunityModel.fromJsonList((response['data'])); + } +} diff --git a/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart b/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart new file mode 100644 index 00000000..e69de29b From 9e8ebf37683a499f4aeb085ee2a357439766a0c5 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Fri, 23 May 2025 07:52:21 -0500 Subject: [PATCH 02/41] no need for models and blocs for now --- .../communities_bloc/communities_bloc.dart | 37 ------------------- .../communities_bloc/communities_event.dart | 10 ----- .../communities_bloc/communities_state.dart | 30 --------------- .../refactor/data/models/community_model.dart | 21 ----------- .../refactor/data/models/device_model.dart | 20 ---------- .../refactor/data/models/space_model.dart | 27 -------------- .../refactor/data/models/sub_space_model.dart | 19 ---------- .../space_managment_remote_source.dart | 17 --------- .../screens/space_mamagment_screen.dart | 0 9 files changed, 181 deletions(-) delete mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart delete mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart delete mode 100644 lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart delete mode 100644 lib/pages/spaces_management/refactor/data/models/community_model.dart delete mode 100644 lib/pages/spaces_management/refactor/data/models/device_model.dart delete mode 100644 lib/pages/spaces_management/refactor/data/models/space_model.dart delete mode 100644 lib/pages/spaces_management/refactor/data/models/sub_space_model.dart delete mode 100644 lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart delete mode 100644 lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart deleted file mode 100644 index 286318d5..00000000 --- a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_bloc.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; - -import '../../data/models/community_model.dart'; -import '../../data/sources/space_managment_remote_source.dart'; - -part 'communities_event.dart'; -part 'communities_state.dart'; - -class CommunitiesBloc extends Bloc { - SpaceManagementRemoteSource spaceManagementRemoteSource = - SpaceManagementRemoteSource(); - CommunitiesBloc() : super(CommunitiesInitial()) { - on((event, emit) { - if (event is FetchCommunitiesEvent) { - _fetchCommunities(emit); - } - }); - } - - Future _fetchCommunities(Emitter emit) async { - String? projectUuid = await ProjectManager.getProjectUUID(); - if (projectUuid == null) { - emit(const CommunitiesError("Project UUID is null")); - return; - } - emit(CommunitiesLoading()); - try { - final communities = - await spaceManagementRemoteSource.fetchCommunities(projectUuid); - emit(CommunitiesLoaded(communities)); - } catch (e) { - emit(CommunitiesError(e.toString())); - } - } -} diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart deleted file mode 100644 index 1d47af23..00000000 --- a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_event.dart +++ /dev/null @@ -1,10 +0,0 @@ -part of 'communities_bloc.dart'; - -sealed class CommunitiesEvent extends Equatable { - const CommunitiesEvent(); - - @override - List get props => []; -} - -class FetchCommunitiesEvent extends CommunitiesEvent {} \ No newline at end of file diff --git a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart b/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart deleted file mode 100644 index 747d8d82..00000000 --- a/lib/pages/spaces_management/refactor/business_logic/communities_bloc/communities_state.dart +++ /dev/null @@ -1,30 +0,0 @@ -part of 'communities_bloc.dart'; - -sealed class CommunitiesState extends Equatable { - const CommunitiesState(); - - @override - List get props => []; -} - -final class CommunitiesInitial extends CommunitiesState {} - -final class CommunitiesLoading extends CommunitiesState {} - -final class CommunitiesLoaded extends CommunitiesState { - final List communities; - - const CommunitiesLoaded(this.communities); - - @override - List get props => [communities]; -} - -final class CommunitiesError extends CommunitiesState { - final String message; - - const CommunitiesError(this.message); - - @override - List get props => [message]; -} diff --git a/lib/pages/spaces_management/refactor/data/models/community_model.dart b/lib/pages/spaces_management/refactor/data/models/community_model.dart deleted file mode 100644 index 88c62ad0..00000000 --- a/lib/pages/spaces_management/refactor/data/models/community_model.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:syncrow_web/pages/spaces_management/refactor/data/models/space_model.dart'; - -class CommunityModel { - String id, name; - List? spaces; - CommunityModel({ - required this.id, - required this.name, - this.spaces, - }); - - factory CommunityModel.fromJson(Map json) => CommunityModel( - id: json['id'], - name: json['name'], - spaces: SpaceModel.fromJsonList(json['spaces']), - ); - - static List fromJsonList(List jsonList) { - return jsonList.map((json) => CommunityModel.fromJson(json)).toList(); - } -} diff --git a/lib/pages/spaces_management/refactor/data/models/device_model.dart b/lib/pages/spaces_management/refactor/data/models/device_model.dart deleted file mode 100644 index 1d000a37..00000000 --- a/lib/pages/spaces_management/refactor/data/models/device_model.dart +++ /dev/null @@ -1,20 +0,0 @@ -class DeviceModel { - String id, name, tag; - String location; - DeviceModel({ - required this.id, - required this.name, - required this.tag, - required this.location, - }); - factory DeviceModel.fromJson(Map json) => DeviceModel( - id: json['id'], - name: json['name'], - tag: json['tag'], - location: json['location'], - ); - - static List fromJsonList(List jsonList) { - return jsonList.map((json) => DeviceModel.fromJson(json)).toList(); - } -} diff --git a/lib/pages/spaces_management/refactor/data/models/space_model.dart b/lib/pages/spaces_management/refactor/data/models/space_model.dart deleted file mode 100644 index c2b6d73c..00000000 --- a/lib/pages/spaces_management/refactor/data/models/space_model.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'device_model.dart'; - -class SpaceModel { - String id, parentId, name; - List? spaces; - List? devices; - - SpaceModel({ - required this.id, - required this.parentId, - required this.name, - this.spaces, - this.devices, - }); - - factory SpaceModel.fromJson(Map json) => SpaceModel( - id: json['id'], - parentId: json['parentId'], - name: json['name'], - spaces: SpaceModel.fromJsonList(json['spaces']), - devices: DeviceModel.fromJsonList(json['devices']), - ); - - static List fromJsonList(List jsonList) { - return jsonList.map((json) => SpaceModel.fromJson(json)).toList(); - } -} diff --git a/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart b/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart deleted file mode 100644 index 2893e0ed..00000000 --- a/lib/pages/spaces_management/refactor/data/models/sub_space_model.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'device_model.dart'; - -class SubSpaceModel { - String id, name; - List? devices; - SubSpaceModel({ - required this.id, - required this.name, - this.devices, - }); - factory SubSpaceModel.fromJson(Map json) => SubSpaceModel( - id: json['id'], - name: json['name'], - devices: DeviceModel.fromJsonList(json['devices']), - ); - static List fromJsonList(List jsonList) { - return jsonList.map((json) => SubSpaceModel.fromJson(json)).toList(); - } -} diff --git a/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart b/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart deleted file mode 100644 index 189cabbf..00000000 --- a/lib/pages/spaces_management/refactor/data/sources/space_managment_remote_source.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:syncrow_web/core/network/end_points.dart'; -import 'package:syncrow_web/core/network/enums.dart'; -import 'package:syncrow_web/core/network/request.dart'; - -import '../models/community_model.dart'; - -class SpaceManagementRemoteSource { - Future> fetchCommunities(String projectUuid) async { - Request request = Request( - EndPoints.fetchCommunities.replaceFirst('{projectUuid}', projectUuid), - method: RequestType.get, - autherized: true, - ); - final response = await request.sendRequest(); - return CommunityModel.fromJsonList((response['data'])); - } -} diff --git a/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart b/lib/pages/spaces_management/refactor/presentation/screens/space_mamagment_screen.dart deleted file mode 100644 index e69de29b..00000000 From e0951aa13ded4900049b30ccfc7c6eb37fac8850 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Fri, 23 May 2025 08:52:53 -0500 Subject: [PATCH 03/41] add interceptor to print request and response --- lib/services/api/http_service.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index b75f05cf..af5b60fe 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -22,6 +22,18 @@ class HTTPService { ); client.interceptors.add(serviceLocator.get()); + // Add this interceptor for logging requests and responses + client.interceptors.add( + LogInterceptor( + request: true, + requestHeader: true, + requestBody: true, + responseHeader: false, + responseBody: true, + error: true, + logPrint: (object) => print(object), + ), + ); return client; } From 0b65c589476a6c4618743f6f171e72fcf6ec07e2 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Mon, 26 May 2025 02:45:58 -0500 Subject: [PATCH 04/41] fix add subspace bugs and plusButton widget --- .../widgets/community_structure_widget.dart | 75 ++++++--- .../widgets/dialogs/create_space_dialog.dart | 147 ++++++++++++------ .../widgets/plus_button_widget.dart | 55 ++++--- .../all_spaces/widgets/space_card_widget.dart | 51 +++--- .../views/create_subspace_model_dialog.dart | 50 +++++- 5 files changed, 242 insertions(+), 136 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart index a93679d0..16ecae36 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart @@ -67,7 +67,8 @@ class _CommunityStructureAreaState extends State { void initState() { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = + widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); _nameController = TextEditingController( text: widget.selectedCommunity?.name ?? '', @@ -96,13 +97,15 @@ class _CommunityStructureAreaState extends State { if (oldWidget.spaces != widget.spaces) { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = + widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); realignTree(); }); } - if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) { + if (widget.selectedSpace != oldWidget.selectedSpace && + widget.selectedSpace != null) { WidgetsBinding.instance.addPostFrameCallback((_) { _moveToSpace(widget.selectedSpace!); }); @@ -185,7 +188,8 @@ class _CommunityStructureAreaState extends State { connection, widget.selectedSpace) ? 1.0 : 0.3, // Adjust opacity - child: CustomPaint(painter: CurvedLinePainter([connection])), + child: CustomPaint( + painter: CurvedLinePainter([connection])), ), for (var entry in spaces.asMap().entries) if (entry.value.status != SpaceStatus.deleted && @@ -195,9 +199,11 @@ class _CommunityStructureAreaState extends State { 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, projectTags: widget.projectTags); @@ -210,8 +216,9 @@ class _CommunityStructureAreaState extends State { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { - final bool isHighlighted = SpaceHelper.isHighlightedSpace( - spaces[index], widget.selectedSpace); + final bool isHighlighted = + SpaceHelper.isHighlightedSpace( + spaces[index], widget.selectedSpace); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, @@ -299,19 +306,25 @@ class _CommunityStructureAreaState extends State { return CreateSpaceDialog( products: widget.products, spaceModels: widget.spaceModels, - allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), + allTags: + TagHelper.getAllTagValues(widget.communities, widget.spaceModels), parentSpace: parentIndex != null ? spaces[parentIndex] : null, projectTags: projectTags, - onCreateSpace: (String name, String icon, List selectedProducts, - SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { + onCreateSpace: (String name, + String icon, + List selectedProducts, + SpaceTemplateModel? spaceModel, + List? subspaces, + List? tags) { setState(() { // Set the first space in the center or use passed position Offset newPosition; if (parentIndex != null) { - newPosition = - getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position + newPosition = getBalancedChildPosition( + spaces[parentIndex]); // Ensure balanced position } else { - newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize); + newPosition = + position ?? ConnectionHelper.getCenterPosition(screenSize); } SpaceModel newSpace = SpaceModel( @@ -360,16 +373,21 @@ class _CommunityStructureAreaState extends State { name: widget.selectedSpace!.name, icon: widget.selectedSpace!.icon, projectTags: widget.projectTags, - parentSpace: - SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces), + parentSpace: SpaceHelper.findSpaceByInternalId( + widget.selectedSpace?.parent?.internalId, spaces), editSpace: widget.selectedSpace, currentSpaceModel: widget.selectedSpace?.spaceModel, tags: widget.selectedSpace?.tags, subspaces: widget.selectedSpace?.subspaces, isEdit: true, - allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), - onCreateSpace: (String name, String icon, List selectedProducts, - SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { + allTags: TagHelper.getAllTagValues( + widget.communities, widget.spaceModels), + onCreateSpace: (String name, + String icon, + List selectedProducts, + SpaceTemplateModel? spaceModel, + List? subspaces, + List? tags) { setState(() { // Update the space's properties widget.selectedSpace!.name = name; @@ -379,7 +397,8 @@ class _CommunityStructureAreaState extends State { widget.selectedSpace!.tags = tags; if (widget.selectedSpace!.status != SpaceStatus.newSpace) { - widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified + widget.selectedSpace!.status = + SpaceStatus.modified; // Mark as modified } for (var space in spaces) { @@ -410,7 +429,8 @@ class _CommunityStructureAreaState extends State { Map idToSpace = {}; void flatten(SpaceModel space) { - if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) { + if (space.status == SpaceStatus.deleted || + space.status == SpaceStatus.parentDeleted) { return; } result.add(space); @@ -532,13 +552,16 @@ class _CommunityStructureAreaState extends State { void _selectSpace(BuildContext context, SpaceModel space) { context.read().add( - SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space), + SelectSpaceEvent( + selectedCommunity: widget.selectedCommunity, + selectedSpace: space), ); } void _deselectSpace(BuildContext context) { context.read().add( - SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null), + SelectSpaceEvent( + selectedCommunity: widget.selectedCommunity, selectedSpace: null), ); } @@ -708,7 +731,8 @@ class _CommunityStructureAreaState extends State { SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent); - duplicated.position = Offset(space.position.dx + 300, space.position.dy + 100); + duplicated.position = + Offset(space.position.dx + 300, space.position.dy + 100); List duplicatedSubtree = []; void collectSubtree(SpaceModel node) { duplicatedSubtree.add(node); @@ -739,7 +763,8 @@ class _CommunityStructureAreaState extends State { } SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) { - final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces); + final duplicatedName = + SpaceHelper.generateUniqueSpaceName(original.name, spaces); final newSpace = SpaceModel( name: duplicatedName, diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index e9dde6f8..b3d1e358 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -82,8 +82,10 @@ class CreateSpaceDialogState extends State { super.initState(); selectedIcon = widget.icon ?? Assets.location; nameController = TextEditingController(text: widget.name ?? ''); - selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; - isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; + selectedProducts = + widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; + isOkButtonEnabled = + enteredName.isNotEmpty || nameController.text.isNotEmpty; if (widget.currentSpaceModel != null) { subspaces = []; tags = []; @@ -96,13 +98,15 @@ class CreateSpaceDialogState extends State { @override Widget build(BuildContext context) { - bool isSpaceModelDisabled = - (tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty); + bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty || + subspaces != null && subspaces!.isNotEmpty); bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null); final screenWidth = MediaQuery.of(context).size.width; return AlertDialog( - title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'), + title: widget.isEdit + ? const Text('Edit Space') + : const Text('Create New Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( width: screenWidth * 0.5, @@ -176,8 +180,8 @@ class CreateSpaceDialogState extends State { isNameFieldInvalid = value.isEmpty; if (!isNameFieldInvalid) { - if (SpaceHelper.isNameConflict( - value, widget.parentSpace, widget.editSpace)) { + if (SpaceHelper.isNameConflict(value, + widget.parentSpace, widget.editSpace)) { isNameFieldExist = true; isOkButtonEnabled = false; } else { @@ -244,7 +248,9 @@ class CreateSpaceDialogState extends State { padding: EdgeInsets.zero, ), onPressed: () { - isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context); + isSpaceModelDisabled + ? null + : _showLinkSpaceModelDialog(context); }, child: ButtonContentWidget( svgAssets: Assets.link, @@ -254,7 +260,8 @@ class CreateSpaceDialogState extends State { ) : Container( width: screenWidth * 0.25, - padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0), + padding: const EdgeInsets.symmetric( + vertical: 10.0, horizontal: 16.0), decoration: BoxDecoration( color: ColorsManager.boxColor, borderRadius: BorderRadius.circular(10), @@ -269,7 +276,8 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodyMedium! - .copyWith(color: ColorsManager.spaceColor), + .copyWith( + color: ColorsManager.spaceColor), ), backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -340,12 +348,12 @@ class CreateSpaceDialogState extends State { onPressed: () async { isTagsAndSubspaceModelDisabled ? null - : _showSubSpaceDialog( - context, enteredName, [], false, widget.products, subspaces); + : _showSubSpaceDialog(context, enteredName, + [], false, widget.products, subspaces); }, child: ButtonContentWidget( icon: Icons.add, - label: 'Create Sub Space', + label: 'Create Sub Spaces', disabled: isTagsAndSubspaceModelDisabled, ), ) @@ -368,16 +376,22 @@ class CreateSpaceDialogState extends State { if (subspaces != null) ...subspaces!.map((subspace) { return Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: + CrossAxisAlignment.start, children: [ SubspaceNameDisplayWidget( text: subspace.subspaceName, validateName: (updatedName) { - bool nameExists = subspaces!.any((s) { - bool isSameId = s.internalId == subspace.internalId; - bool isSameName = - s.subspaceName.trim().toLowerCase() == - updatedName.trim().toLowerCase(); + bool nameExists = + subspaces!.any((s) { + bool isSameId = s.internalId == + subspace.internalId; + bool isSameName = s.subspaceName + .trim() + .toLowerCase() == + updatedName + .trim() + .toLowerCase(); return !isSameId && isSameName; }); @@ -386,7 +400,8 @@ class CreateSpaceDialogState extends State { }, onNameChanged: (updatedName) { setState(() { - subspace.subspaceName = updatedName; + subspace.subspaceName = + updatedName; }); }, ), @@ -395,8 +410,8 @@ class CreateSpaceDialogState extends State { }), EditChip( onTap: () async { - _showSubSpaceDialog(context, enteredName, [], true, - widget.products, subspaces); + _showSubSpaceDialog(context, enteredName, + [], true, widget.products, subspaces); }, ) ], @@ -405,7 +420,9 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 10), (tags?.isNotEmpty == true || - subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true) + subspaces?.any((subspace) => + subspace.tags?.isNotEmpty == true) == + true) ? SizedBox( width: screenWidth * 0.25, child: Container( @@ -425,14 +442,16 @@ class CreateSpaceDialogState extends State { // Combine tags from spaceModel and subspaces ...TagHelper.groupTags([ ...?tags, - ...?subspaces?.expand((subspace) => subspace.tags ?? []) + ...?subspaces?.expand( + (subspace) => subspace.tags ?? []) ]).entries.map( (entry) => Chip( avatar: SizedBox( width: 24, height: 24, child: SvgPicture.asset( - entry.key.icon ?? 'assets/icons/gateway.svg', + entry.key.icon ?? + 'assets/icons/gateway.svg', fit: BoxFit.contain, ), ), @@ -441,11 +460,15 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodySmall - ?.copyWith(color: ColorsManager.spaceColor), + ?.copyWith( + color: ColorsManager + .spaceColor), ), - backgroundColor: ColorsManager.whiteColors, + backgroundColor: + ColorsManager.whiteColors, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: + BorderRadius.circular(16), side: const BorderSide( color: ColorsManager.spaceColor, ), @@ -460,15 +483,18 @@ class CreateSpaceDialogState extends State { products: widget.products, subspaces: subspaces, allTags: widget.allTags, - addedProducts: - TagHelper.createInitialSelectedProductsForTags( + addedProducts: TagHelper + .createInitialSelectedProductsForTags( tags ?? [], subspaces), title: 'Edit Device', - initialTags: TagHelper.generateInitialForTags( - spaceTags: tags, subspaces: subspaces), + initialTags: + TagHelper.generateInitialForTags( + spaceTags: tags, + subspaces: subspaces), spaceName: widget.name ?? '', projectTags: widget.projectTags, - onSave: (updatedTags, updatedSubspaces) { + onSave: + (updatedTags, updatedSubspaces) { setState(() { tags = updatedTags; subspaces = updatedSubspaces; @@ -529,17 +555,25 @@ class CreateSpaceDialogState extends State { } else if (isNameFieldExist) { return; } else { - String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); + String newName = enteredName.isNotEmpty + ? enteredName + : (widget.name ?? ''); if (newName.isNotEmpty) { - widget.onCreateSpace(newName, selectedIcon, selectedProducts, - selectedSpaceModel, subspaces, tags); + widget.onCreateSpace( + newName, + selectedIcon, + selectedProducts, + selectedSpaceModel, + subspaces, + tags); Navigator.of(context).pop(); } } }, borderRadius: 10, - backgroundColor: - isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor, + backgroundColor: isOkButtonEnabled + ? ColorsManager.secondaryColor + : ColorsManager.grayColor, foregroundColor: ColorsManager.whiteColors, child: const Text('OK'), ), @@ -586,24 +620,31 @@ class CreateSpaceDialogState extends State { ); } - void _showSubSpaceDialog(BuildContext context, String name, final List? spaceTags, - bool isEdit, List? products, final List? existingSubSpaces) { + void _showSubSpaceDialog( + BuildContext context, + String name, + final List? spaceTags, + bool isEdit, + List? products, + final List? existingSubSpaces) { showDialog( context: context, builder: (BuildContext context) { return CreateSubSpaceDialog( spaceName: name, - dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space', + dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces', products: products, existingSubSpaces: existingSubSpaces, onSave: (slectedSubspaces) { final List tagsToAppendToSpace = []; - if (slectedSubspaces != null) { - final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet(); + if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) { + final updatedIds = + slectedSubspaces.map((s) => s.internalId).toSet(); if (existingSubSpaces != null) { - final deletedSubspaces = - existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList(); + final deletedSubspaces = existingSubSpaces + .where((s) => !updatedIds.contains(s.internalId)) + .toList(); for (var s in deletedSubspaces) { if (s.tags != null) { tagsToAppendToSpace.addAll(s.tags!); @@ -623,15 +664,16 @@ class CreateSpaceDialogState extends State { ); } - void _showTagCreateDialog( - BuildContext context, String name, bool isEdit, List? products) { + void _showTagCreateDialog(BuildContext context, String name, bool isEdit, + List? products) { isEdit ? showDialog( context: context, builder: (BuildContext context) { return AssignTagDialog( title: 'Edit Device', - addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces), + addedProducts: TagHelper.createInitialSelectedProductsForTags( + tags, subspaces), spaceName: name, products: products, subspaces: subspaces, @@ -646,7 +688,8 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == selectedSubspace.subspaceName) { + if (subspace.subspaceName == + selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } @@ -670,7 +713,8 @@ class CreateSpaceDialogState extends State { allTags: widget.allTags, projectTags: widget.projectTags, initialSelectedProducts: - TagHelper.createInitialSelectedProductsForTags(tags, subspaces), + TagHelper.createInitialSelectedProductsForTags( + tags, subspaces), onSave: (selectedSpaceTags, selectedSubspaces) { setState(() { tags = selectedSpaceTags; @@ -680,7 +724,8 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == selectedSubspace.subspaceName) { + if (subspace.subspaceName == + selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } diff --git a/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart index 40be7284..280816a0 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart @@ -17,36 +17,33 @@ class PlusButtonWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return Positioned( - left: offset.dx, - top: offset.dy, - child: GestureDetector( - onTap: () { - Offset newPosition; - switch (direction) { - case 'left': - newPosition = const Offset(-200, 0); - break; - case 'right': - newPosition = const Offset(200, 0); - break; - case 'down': - newPosition = const Offset(0, 150); - break; - default: - newPosition = Offset.zero; - } - onButtonTap(index, newPosition, direction); - }, - child: Container( - width: 30, - height: 30, - decoration: const BoxDecoration( - color: ColorsManager.spaceColor, - shape: BoxShape.circle, - ), - child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20), + return GestureDetector( + onTap: () { + Offset newPosition; + switch (direction) { + case 'left': + newPosition = const Offset(-200, 0); + break; + case 'right': + newPosition = const Offset(200, 0); + break; + case 'down': + newPosition = const Offset(0, 150); + break; + default: + newPosition = Offset.zero; + } + onButtonTap(index, newPosition, direction); + }, + child: Container( + width: 30, + height: 30, + decoration: const BoxDecoration( + color: ColorsManager.spaceColor, + shape: BoxShape.circle, ), + child: + const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20), ), ); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart index f3a476b2..49df9daa 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart @@ -25,35 +25,34 @@ class SpaceCardWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onPanUpdate: (details) { - // Call the provided callback to update the position - final newPosition = position + details.delta; - onPositionChanged(newPosition); - }, - 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); - }, + return MouseRegion( + onEnter: (_) => onHoverChanged(index, true), + onExit: (_) => onHoverChanged(index, false), + child: SizedBox( + width: 140, // Make sure this covers both card and plus button + height: 90, child: Stack( - clipBehavior: Clip - .none, // Allow hovering elements to be displayed outside the boundary + clipBehavior: Clip.none, children: [ - buildSpaceContainer(index), // Build the space container - if (isHovered) ...[ - PlusButtonWidget( - index: index, - direction: 'down', - offset: const Offset(63, 50), - onButtonTap: onButtonTap, + // Main card + Container( + width: 140, + height: 80, + alignment: Alignment.center, + color: Colors.transparent, + child: buildSpaceContainer(index), + ), + // Plus button (NO inner Positioned!) + if (isHovered) + Align( + alignment: Alignment.bottomCenter, + child: PlusButtonWidget( + index: index, + direction: 'down', + offset: Offset.zero, + onButtonTap: onButtonTap, + ), ), - ], ], ), ), diff --git a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart index 948028ed..483382af 100644 --- a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart @@ -79,6 +79,22 @@ class _CreateSubSpaceDialogState extends State { color: ColorsManager.blackColor, ), ), + Row( + children: [ + Text( + 'press Enter to Save', + style: context.textTheme.headlineLarge?.copyWith( + fontWeight: FontWeight.w500, + fontSize: 10, + color: ColorsManager.grayColor, + ), + ), + const SizedBox( + width: 5, + ), + const Icon(Icons.save_as_sharp, size: 10), + ], + ), const SizedBox(height: 16), Container( width: context.screenWidth * 0.35, @@ -101,13 +117,15 @@ class _CreateSubSpaceDialogState extends State { final index = entry.key; final subSpace = entry.value; - final lowerName = subSpace.subspaceName.toLowerCase(); + final lowerName = + subSpace.subspaceName.toLowerCase(); final duplicateIndices = state.subSpaces .asMap() .entries .where((e) => - e.value.subspaceName.toLowerCase() == lowerName) + e.value.subspaceName.toLowerCase() == + lowerName) .map((e) => e.key) .toList(); final isDuplicate = duplicateIndices.length > 1 && @@ -182,10 +200,32 @@ class _CreateSubSpaceDialogState extends State { Expanded( child: DefaultButton( onPressed: state.errorMessage.isEmpty - ? () { - final subSpacesBloc = context.read(); - final subSpaces = subSpacesBloc.state.subSpaces; + ? () async { + final trimmedValue = + _subspaceNameController.text.trim(); + + final subSpacesBloc = + context.read(); + if (trimmedValue.isNotEmpty) { + subSpacesBloc.add( + AddSubSpace( + SubspaceModel( + subspaceName: trimmedValue, + disabled: false, + ), + ), + ); + _subspaceNameController.clear(); + } + + await Future.delayed( + const Duration(milliseconds: 10)); + final subSpaces = + subSpacesBloc.state.subSpaces; + // if (subSpaces.isNotEmpty) { widget.onSave?.call(subSpaces); + // } + Navigator.of(context).pop(); } : null, From c97dd40b058b15330d57aeb82ee18602376c6e56 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Mon, 26 May 2025 06:11:06 -0500 Subject: [PATCH 05/41] add toSelected function to convert from product to selectedProduct --- .../all_spaces/model/product_model.dart | 11 +++++++++++ .../tag_model/widgets/device_type_tile_widget.dart | 8 ++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/model/product_model.dart b/lib/pages/spaces_management/all_spaces/model/product_model.dart index a4ebd550..8f905032 100644 --- a/lib/pages/spaces_management/all_spaces/model/product_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/product_model.dart @@ -1,5 +1,7 @@ import 'package:syncrow_web/utils/constants/assets.dart'; +import 'selected_product_model.dart'; + class ProductModel { final String uuid; final String catName; @@ -38,6 +40,15 @@ class ProductModel { }; } + SelectedProduct toSelectedProduct(int count) { + return SelectedProduct( + productId: uuid, + count: count, + productName: name!, + product: this, + ); + } + static String _mapIconToProduct(String prodType) { const iconMapping = { '1G': Assets.Gang1SwitchIcon, diff --git a/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart b/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart index 7d103cdb..3d645d7c 100644 --- a/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart +++ b/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart @@ -54,10 +54,10 @@ class DeviceTypeTileWidget extends StatelessWidget { onCountChanged: (newCount) { context.read().add( UpdateProductCountEvent( - productId: product.uuid, - count: newCount, - productName: product.catName, - product: product), + selectedProduct: product.toSelectedProduct( + newCount, + ), + ), ); }, ), From 766a39f161c5f5cbc50041bd2c1b312acf867d86 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Mon, 26 May 2025 06:11:48 -0500 Subject: [PATCH 06/41] use selected model in add device event --- lib/common/tag_dialog_textfield_dropdown.dart | 23 +++++++++++---- .../tag_model/bloc/add_device_model_bloc.dart | 29 +++++-------------- .../bloc/add_device_type_model_event.dart | 11 ++----- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/lib/common/tag_dialog_textfield_dropdown.dart b/lib/common/tag_dialog_textfield_dropdown.dart index 219e03ce..6bc22fc0 100644 --- a/lib/common/tag_dialog_textfield_dropdown.dart +++ b/lib/common/tag_dialog_textfield_dropdown.dart @@ -17,7 +17,8 @@ class TagDialogTextfieldDropdown extends StatefulWidget { }) : super(key: key); @override - _DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState(); + _DialogTextfieldDropdownState createState() => + _DialogTextfieldDropdownState(); } class _DialogTextfieldDropdownState extends State { @@ -36,6 +37,12 @@ class _DialogTextfieldDropdownState extends State { _focusNode.addListener(() { if (!_focusNode.hasFocus) { + // Call onSelected when focus is lost + final selectedTag = _filteredItems.firstWhere( + (tag) => tag.tag == _controller.text, + orElse: () => Tag(tag: _controller.text), + ); + widget.onSelected(selectedTag); _closeDropdown(); } }); @@ -43,7 +50,9 @@ class _DialogTextfieldDropdownState extends State { void _filterItems() { setState(() { - _filteredItems = widget.items.where((tag) => tag.product?.uuid == widget.product).toList(); + _filteredItems = widget.items + .where((tag) => tag.product?.uuid == widget.product) + .toList(); }); } @@ -112,7 +121,9 @@ class _DialogTextfieldDropdownState extends State { style: Theme.of(context) .textTheme .bodyMedium - ?.copyWith(color: ColorsManager.textPrimaryColor)), + ?.copyWith( + color: ColorsManager + .textPrimaryColor)), onTap: () { _controller.text = tag.tag ?? ''; widget.onSelected(tag); @@ -156,13 +167,15 @@ class _DialogTextfieldDropdownState extends State { controller: _controller, focusNode: _focusNode, onFieldSubmitted: (value) { - final selectedTag = _filteredItems.firstWhere((tag) => tag.tag == value, + final selectedTag = _filteredItems.firstWhere( + (tag) => tag.tag == value, orElse: () => Tag(tag: value)); widget.onSelected(selectedTag); _closeDropdown(); }, onTapOutside: (event) { - widget.onSelected(_filteredItems.firstWhere((tag) => tag.tag == _controller.text, + widget.onSelected(_filteredItems.firstWhere( + (tag) => tag.tag == _controller.text, orElse: () => Tag(tag: _controller.text))); _closeDropdown(); }, diff --git a/lib/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart b/lib/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart index 9c617a12..a0081c22 100644 --- a/lib/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart +++ b/lib/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart @@ -24,37 +24,22 @@ class AddDeviceTypeModelBloc if (currentState is AddDeviceModelLoaded) { final existingProduct = currentState.selectedProducts.firstWhere( - (p) => p.productId == event.productId, - orElse: () => SelectedProduct( - productId: event.productId, - count: 0, - productName: event.productName, - product: event.product, - ), + (p) => p.productId == event.selectedProduct.productId, + orElse: () => event.selectedProduct, ); List updatedProducts; - if (event.count > 0) { + if (event.selectedProduct.count > 0) { if (!currentState.selectedProducts.contains(existingProduct)) { updatedProducts = [ ...currentState.selectedProducts, - SelectedProduct( - productId: event.productId, - count: event.count, - productName: event.productName, - product: event.product, - ), + event.selectedProduct, ]; } else { updatedProducts = currentState.selectedProducts.map((p) { - if (p.productId == event.productId) { - return SelectedProduct( - productId: p.productId, - count: event.count, - productName: p.productName, - product: p.product, - ); + if (p.productId == event.selectedProduct.productId) { + return event.selectedProduct; } return p; }).toList(); @@ -62,7 +47,7 @@ class AddDeviceTypeModelBloc } else { // Remove the product if the count is 0 updatedProducts = currentState.selectedProducts - .where((p) => p.productId != event.productId) + .where((p) => p.productId != event.selectedProduct.productId) .toList(); } diff --git a/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart b/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart index b9018b2b..27f183a6 100644 --- a/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart +++ b/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart @@ -10,20 +10,15 @@ abstract class AddDeviceTypeModelEvent extends Equatable { List get props => []; } - class UpdateProductCountEvent extends AddDeviceTypeModelEvent { - final String productId; - final int count; - final String productName; - final ProductModel product; + final SelectedProduct selectedProduct; - UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product}); + UpdateProductCountEvent({required this.selectedProduct}); @override - List get props => [productId, count]; + List get props => [selectedProduct]; } - class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent { final List initialTags; final List addedProducts; From 644fe56478d79c2159a54d2a3dfa27eb48d7274e Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Mon, 26 May 2025 06:12:24 -0500 Subject: [PATCH 07/41] seperaate UI into widgets --- .../views/assign_tag_models_dialog.dart | 257 ++---------------- .../views/widgets/RowOfCancelSaveWidget.dart | 152 +++++++++++ .../widgets/assign_tags_tables_widget.dart | 172 ++++++++++++ 3 files changed, 344 insertions(+), 237 deletions(-) create mode 100644 lib/pages/spaces_management/assign_tag_models/views/widgets/RowOfCancelSaveWidget.dart create mode 100644 lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index 85be3bf3..873c7dd6 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -1,9 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.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/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; @@ -12,11 +8,9 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assig import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart'; -import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; -import 'package:uuid/uuid.dart'; + +import 'widgets/assign_tags_tables_widget.dart'; class AssignTagModelsDialog extends StatelessWidget { final List? products; @@ -53,8 +47,10 @@ class AssignTagModelsDialog extends StatelessWidget { @override Widget build(BuildContext context) { - final List locations = - (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); + final List locations = (subspaces ?? []) + .map((subspace) => subspace.subspaceName) + .toList() + ..add('Main Space'); return BlocProvider( create: (_) => AssignTagModelBloc(projectTags) @@ -78,137 +74,9 @@ class AssignTagModelsDialog extends StatelessWidget { content: SingleChildScrollView( child: Column( children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: DataTable( - headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), - key: ValueKey(state.tags.length), - border: TableBorder.all( - color: ColorsManager.dataHeaderGrey, - width: 1, - borderRadius: BorderRadius.circular(20), - ), - columns: [ - DataColumn( - label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Device', - style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - numeric: false, - label: - Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Location', - style: Theme.of(context).textTheme.bodyMedium)), - ], - rows: state.tags.isEmpty - ? [ - DataRow(cells: [ - DataCell( - Center( - child: Text('No Devices Available', - style: - Theme.of(context).textTheme.bodyMedium?.copyWith( - color: ColorsManager.lightGrayColor, - )), - ), - ), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - ]) - ] - : List.generate(state.tags.length, (index) { - final tag = state.tags[index]; - final controller = controllers[index]; - - return DataRow( - cells: [ - DataCell(Text((index + 1).toString())), - DataCell( - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - tag.product?.name ?? 'Unknown', - overflow: TextOverflow.ellipsis, - )), - const SizedBox(width: 10), - Container( - width: 20.0, - height: 20.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.0, - ), - ), - child: IconButton( - icon: const Icon( - Icons.close, - color: ColorsManager.lightGreyColor, - size: 16, - ), - onPressed: () { - context.read().add( - DeleteTagModel( - tagToDelete: tag, tags: state.tags)); - controllers.removeAt(index); - }, - tooltip: 'Delete Tag', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - ), - ], - ), - ), - DataCell( - Container( - alignment: Alignment - .centerLeft, // Align cell content to the left - child: SizedBox( - width: double.infinity, - child: TagDialogTextfieldDropdown( - key: ValueKey( - 'dropdown_${const Uuid().v4()}_$index'), - product: tag.product?.uuid ?? 'Unknown', - items: state.updatedTags, - initialValue: tag, - onSelected: (value) { - controller.text = value.tag ?? ''; - context.read().add(UpdateTag( - index: index, - tag: value, - )); - }, - ), - ), - ), - ), - DataCell( - SizedBox( - width: double.infinity, - child: DialogDropdown( - items: locations, - selectedValue: tag.location ?? 'Main Space', - onSelected: (value) { - context - .read() - .add(UpdateLocation( - index: index, - location: value, - )); - }, - )), - ), - ], - ); - }), - ), + AssignTagsTable( + controllers: controllers, + locations: locations, ), if (state.errorMessage != null) Text(state.errorMessage!, @@ -220,102 +88,17 @@ class AssignTagModelsDialog extends StatelessWidget { ), ), actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const SizedBox(width: 10), - Expanded( - child: Builder( - builder: (buttonContext) => CancelButton( - label: 'Add New Device', - onPressed: () async { - final updatedTags = List.from(state.tags); - final result = - TagHelper.updateSubspaceTagModels(updatedTags, subspaces); - - final processedTags = result['updatedTags'] as List; - final processedSubspaces = List.from( - result['subspaces'] as List); - - if (context.mounted) { - Navigator.of(context).pop(); - - await showDialog( - barrierDismissible: false, - context: context, - builder: (dialogContext) => AddDeviceTypeModelWidget( - products: products, - subspaces: processedSubspaces, - isCreate: false, - initialSelectedProducts: - TagHelper.createInitialSelectedProducts( - processedTags, processedSubspaces), - allTags: allTags, - spaceName: spaceName, - otherSpaceModels: otherSpaceModels, - spaceTagModels: processedTags, - pageContext: pageContext, - projectTags: projectTags, - spaceModel: SpaceTemplateModel( - modelName: spaceName, - tags: updatedTags, - uuid: spaceModel?.uuid, - internalId: spaceModel?.internalId, - subspaceModels: processedSubspaces)), - ); - } - }, - ), - ), - ), - const SizedBox(width: 10), - Expanded( - child: DefaultButton( - borderRadius: 10, - backgroundColor: ColorsManager.secondaryColor, - foregroundColor: state.isSaveEnabled - ? ColorsManager.whiteColors - : ColorsManager.whiteColorsWithOpacity, - onPressed: state.isSaveEnabled - ? () async { - final updatedTags = List.from(state.tags); - - final result = - TagHelper.updateSubspaceTagModels(updatedTags, subspaces); - - final processedTags = result['updatedTags'] as List; - final processedSubspaces = List.from( - result['subspaces'] as List); - - Navigator.of(context).popUntil((route) => route.isFirst); - - await showDialog( - context: context, - builder: (BuildContext dialogContext) { - return CreateSpaceModelDialog( - products: products, - allSpaceModels: allSpaceModels, - allTags: allTags, - projectTags: projectTags, - pageContext: pageContext, - otherSpaceModels: otherSpaceModels, - spaceModel: SpaceTemplateModel( - modelName: spaceName, - tags: processedTags, - uuid: spaceModel?.uuid, - internalId: spaceModel?.internalId, - subspaceModels: processedSubspaces), - ); - }, - ); - } - : null, - child: const Text('Save'), - ), - ), - const SizedBox(width: 10), - ], - ), + // RowOfNextCancelWidget( + // subspaces: subspaces, + // products: products, + // allTags: allTags, + // spaceName: spaceName, + // otherSpaceModels: otherSpaceModels, + // pageContext: pageContext, + // projectTags: projectTags, + // spaceModel: spaceModel, + // allSpaceModels: allSpaceModels, + // ), ], ); } else if (state is AssignTagModelLoading) { diff --git a/lib/pages/spaces_management/assign_tag_models/views/widgets/RowOfCancelSaveWidget.dart b/lib/pages/spaces_management/assign_tag_models/views/widgets/RowOfCancelSaveWidget.dart new file mode 100644 index 00000000..9b2a1367 --- /dev/null +++ b/lib/pages/spaces_management/assign_tag_models/views/widgets/RowOfCancelSaveWidget.dart @@ -0,0 +1,152 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../../../../../utils/color_manager.dart'; +import '../../../../common/buttons/cancel_button.dart'; +import '../../../../common/buttons/default_button.dart'; +import '../../../all_spaces/model/product_model.dart'; +import '../../../all_spaces/model/tag.dart'; +import '../../../helper/tag_helper.dart'; +import '../../../space_model/models/space_template_model.dart'; +import '../../../space_model/models/subspace_template_model.dart'; +import '../../../space_model/widgets/dialog/create_space_model_dialog.dart'; +import '../../../tag_model/views/add_device_type_model_widget.dart'; +import '../../bloc/assign_tag_model_bloc.dart'; +import '../../bloc/assign_tag_model_state.dart'; + +class RowOfSaveCancelWidget extends StatelessWidget { + const RowOfSaveCancelWidget({ + super.key, + required this.subspaces, + required this.products, + required this.allTags, + required this.spaceName, + required this.otherSpaceModels, + required this.pageContext, + required this.projectTags, + required this.spaceModel, + required this.allSpaceModels, + }); + + final List? subspaces; + final List? products; + final List? allTags; + final String spaceName; + final List? otherSpaceModels; + final BuildContext? pageContext; + final List projectTags; + final SpaceTemplateModel? spaceModel; + final List? allSpaceModels; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AssignTagModelLoaded) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const SizedBox(width: 10), + Expanded( + child: Builder( + builder: (buttonContext) => CancelButton( + label: 'Add New Device', + onPressed: () async { + final updatedTags = List.from(state.tags); + final result = TagHelper.updateSubspaceTagModels( + updatedTags, subspaces); + + final processedTags = result['updatedTags'] as List; + final processedSubspaces = + List.from( + result['subspaces'] as List); + + if (context.mounted) { + Navigator.of(context).pop(); + + await showDialog( + barrierDismissible: false, + context: context, + builder: (dialogContext) => AddDeviceTypeModelWidget( + products: products, + subspaces: processedSubspaces, + isCreate: false, + initialSelectedProducts: + TagHelper.createInitialSelectedProducts( + processedTags, processedSubspaces), + allTags: allTags, + spaceName: spaceName, + otherSpaceModels: otherSpaceModels, + spaceTagModels: processedTags, + pageContext: pageContext, + projectTags: projectTags, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: updatedTags, + uuid: spaceModel?.uuid, + internalId: spaceModel?.internalId, + subspaceModels: processedSubspaces)), + ); + } + }, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + borderRadius: 10, + backgroundColor: ColorsManager.secondaryColor, + foregroundColor: state.isSaveEnabled + ? ColorsManager.whiteColors + : ColorsManager.whiteColorsWithOpacity, + onPressed: state.isSaveEnabled + ? () async { + final updatedTags = List.from(state.tags); + + final result = TagHelper.updateSubspaceTagModels( + updatedTags, subspaces); + + final processedTags = + result['updatedTags'] as List; + final processedSubspaces = + List.from( + result['subspaces'] as List); + + Navigator.of(context) + .popUntil((route) => route.isFirst); + + await showDialog( + context: context, + builder: (BuildContext dialogContext) { + return CreateSpaceModelDialog( + products: products, + allSpaceModels: allSpaceModels, + allTags: allTags, + projectTags: projectTags, + pageContext: pageContext, + otherSpaceModels: otherSpaceModels, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: processedTags, + uuid: spaceModel?.uuid, + internalId: spaceModel?.internalId, + subspaceModels: processedSubspaces), + ); + }, + ); + } + : null, + child: const Text('Save'), + ), + ), + const SizedBox(width: 10), + ], + ); + } else { + return const SizedBox(); + } + }, + ); + } +} diff --git a/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart b/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart new file mode 100644 index 00000000..eaddc26c --- /dev/null +++ b/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:uuid/uuid.dart'; + +import '../../../../../common/dialog_dropdown.dart'; +import '../../../../../common/tag_dialog_textfield_dropdown.dart'; +import '../../../../../utils/color_manager.dart'; +import '../../bloc/assign_tag_model_bloc.dart'; +import '../../bloc/assign_tag_model_event.dart'; +import '../../bloc/assign_tag_model_state.dart'; + +class AssignTagsTable extends StatelessWidget { + const AssignTagsTable({ + super.key, + required this.controllers, + required this.locations, + }); + + final List controllers; + final List locations; + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) { + if (state is AssignTagModelLoaded) { + return ClipRRect( + borderRadius: BorderRadius.circular(20), + child: DataTable( + headingRowColor: + WidgetStateProperty.all(ColorsManager.dataHeaderGrey), + key: ValueKey(state.tags.length), + border: TableBorder.all( + color: ColorsManager.dataHeaderGrey, + width: 1, + borderRadius: BorderRadius.circular(20), + ), + columns: [ + DataColumn( + label: Text('#', + style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + label: Text('Device', + style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + numeric: false, + label: Text('Tag', + style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + label: Text('Location', + style: Theme.of(context).textTheme.bodyMedium)), + ], + rows: state.tags.isEmpty + ? [ + DataRow(cells: [ + DataCell( + Center( + child: Text('No Devices Available', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith( + color: ColorsManager.lightGrayColor, + )), + ), + ), + const DataCell(SizedBox()), + const DataCell(SizedBox()), + const DataCell(SizedBox()), + ]) + ] + : List.generate(state.tags.length, (index) { + final tag = state.tags[index]; + final controller = controllers[index]; + + return DataRow( + cells: [ + DataCell(Text((index + 1).toString())), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + tag.product?.name ?? 'Unknown', + overflow: TextOverflow.ellipsis, + )), + const SizedBox(width: 10), + Container( + width: 20.0, + height: 20.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1.0, + ), + ), + child: IconButton( + icon: const Icon( + Icons.close, + color: ColorsManager.lightGreyColor, + size: 16, + ), + onPressed: () { + context.read().add( + DeleteTagModel( + tagToDelete: tag, + tags: state.tags)); + controllers.removeAt(index); + }, + tooltip: 'Delete Tag', + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + ), + ], + ), + ), + DataCell( + Container( + alignment: Alignment + .centerLeft, // Align cell content to the left + child: SizedBox( + width: double.infinity, + child: TagDialogTextfieldDropdown( + key: ValueKey( + 'dropdown_${const Uuid().v4()}_$index'), + product: tag.product?.uuid ?? 'Unknown', + items: state.updatedTags, + initialValue: tag, + onSelected: (value) { + controller.text = value.tag ?? ''; + context + .read() + .add(UpdateTag( + index: index, + tag: value, + )); + }, + ), + ), + ), + ), + DataCell( + SizedBox( + width: double.infinity, + child: DialogDropdown( + items: locations, + selectedValue: tag.location ?? 'Main Space', + onSelected: (value) { + context + .read() + .add(UpdateLocation( + index: index, + location: value, + )); + }, + )), + ), + ], + ); + }), + ), + ); + } else { + return const SizedBox(); + } + }, + ); + } +} From d69d867120f297988a7a13d6d30a0f28df6bf336 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Mon, 26 May 2025 06:27:48 -0500 Subject: [PATCH 08/41] use assign_tag_dialog_widgetwidget --- .../views/assign_tag_models_dialog.dart | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index 873c7dd6..6343782c 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/space_tem import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'widgets/RowOfCancelSaveWidget.dart'; import 'widgets/assign_tags_tables_widget.dart'; class AssignTagModelsDialog extends StatelessWidget { @@ -88,17 +89,17 @@ class AssignTagModelsDialog extends StatelessWidget { ), ), actions: [ - // RowOfNextCancelWidget( - // subspaces: subspaces, - // products: products, - // allTags: allTags, - // spaceName: spaceName, - // otherSpaceModels: otherSpaceModels, - // pageContext: pageContext, - // projectTags: projectTags, - // spaceModel: spaceModel, - // allSpaceModels: allSpaceModels, - // ), + RowOfSaveCancelWidget( + subspaces: subspaces, + products: products, + allTags: allTags, + spaceName: spaceName, + otherSpaceModels: otherSpaceModels, + pageContext: pageContext, + projectTags: projectTags, + spaceModel: spaceModel, + allSpaceModels: allSpaceModels, + ), ], ); } else if (state is AssignTagModelLoading) { From 056e7372e08ff3777ead092f27ba0cac5255f4ae Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Tue, 27 May 2025 01:15:30 -0500 Subject: [PATCH 09/41] use assign table as widget --- .../assign_tag/views/assign_tag_dialog.dart | 178 +++--------- .../views/assign_tag_models_dialog.dart | 29 ++ .../widgets/assign_tags_tables_widget.dart | 275 ++++++++---------- 3 files changed, 201 insertions(+), 281 deletions(-) diff --git a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart index fd1454e5..a244d235 100644 --- a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart +++ b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart @@ -12,6 +12,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart'; +import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:uuid/uuid.dart'; @@ -44,8 +45,10 @@ class AssignTagDialog extends StatelessWidget { @override Widget build(BuildContext context) { - final List locations = - (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); + final List locations = (subspaces ?? []) + .map((subspace) => subspace.subspaceName) + .toList() + ..add('Main Space'); return BlocProvider( create: (_) => AssignTagBloc(projectTags) @@ -67,131 +70,31 @@ class AssignTagDialog extends StatelessWidget { content: SingleChildScrollView( child: Column( children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: DataTable( - headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), - key: ValueKey(state.tags.length), - border: TableBorder.all( - color: ColorsManager.dataHeaderGrey, - width: 1, - borderRadius: BorderRadius.circular(20), - ), - columns: [ - DataColumn( - label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Device', style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - numeric: false, - label: Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: - Text('Location', style: Theme.of(context).textTheme.bodyMedium)), - ], - rows: state.tags.isEmpty - ? [ - DataRow(cells: [ - DataCell( - Center( - child: Text('No Data Available', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: ColorsManager.lightGrayColor, - )), - ), - ), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - ]) - ] - : List.generate(state.tags.length, (index) { - final tag = state.tags[index]; - final controller = controllers[index]; + AssignTagsTable( + controllers: controllers, + locations: locations, + tags: state.tags, + updatedTags: state.updatedTags, + onDeleteDevice: ({required index, required tag}) { + context + .read() + .add(DeleteTag(tagToDelete: tag, tags: state.tags)); - return DataRow( - cells: [ - DataCell(Text((index + 1).toString())), - DataCell( - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - tag.product?.name ?? 'Unknown', - overflow: TextOverflow.ellipsis, - )), - const SizedBox(width: 10), - Container( - width: 20.0, - height: 20.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.0, - ), - ), - child: IconButton( - icon: const Icon( - Icons.close, - color: ColorsManager.lightGreyColor, - size: 16, - ), - onPressed: () { - context.read().add( - DeleteTag(tagToDelete: tag, tags: state.tags)); - - controllers.removeAt(index); - }, - tooltip: 'Delete Tag', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - ), - ], - ), - ), - DataCell( - Container( - alignment: - Alignment.centerLeft, // Align cell content to the left - child: SizedBox( - width: double.infinity, - child: TagDialogTextfieldDropdown( - key: ValueKey('dropdown_${const Uuid().v4()}_$index'), - items: state.updatedTags, - product: tag.product?.uuid ?? 'Unknown', - initialValue: tag, - onSelected: (value) { - controller.text = value.tag ?? ''; - context.read().add(UpdateTagEvent( - index: index, - tag: value, - )); - }, - ), - ), - ), - ), - DataCell( - SizedBox( - width: double.infinity, - child: DialogDropdown( - items: locations, - selectedValue: tag.location ?? 'Main Space', - onSelected: (value) { - context.read().add(UpdateLocation( - index: index, - location: value, - )); - }, - )), - ), - ], - ); - }), - ), + controllers.removeAt(index); + }, + onTagDropDownSelected: ({required index, required tag}) { + context.read().add(UpdateTagEvent( + index: index, + tag: tag, + )); + }, + onLocationDropDownSelected: ( + {required index, required location}) { + context.read().add(UpdateLocation( + index: index, + location: location, + )); + }, ), if (state.errorMessage != null) Text(state.errorMessage!, @@ -213,11 +116,13 @@ class AssignTagDialog extends StatelessWidget { label: 'Add New Device', onPressed: () async { final updatedTags = List.from(state.tags); - final result = TagHelper.processTags(updatedTags, subspaces); + final result = + TagHelper.processTags(updatedTags, subspaces); - final processedTags = result['updatedTags'] as List; - final processedSubspaces = - List.from(result['subspaces'] as List); + final processedTags = + result['updatedTags'] as List; + final processedSubspaces = List.from( + result['subspaces'] as List); Navigator.of(context).pop(); @@ -227,8 +132,8 @@ class AssignTagDialog extends StatelessWidget { products: products, subspaces: processedSubspaces, projectTags: projectTags, - initialSelectedProducts: - TagHelper.createInitialSelectedProductsForTags( + initialSelectedProducts: TagHelper + .createInitialSelectedProductsForTags( processedTags, processedSubspaces), spaceName: spaceName, spaceTags: processedTags, @@ -252,11 +157,14 @@ class AssignTagDialog extends StatelessWidget { onPressed: state.isSaveEnabled ? () async { final updatedTags = List.from(state.tags); - final result = TagHelper.processTags(updatedTags, subspaces); + final result = TagHelper.processTags( + updatedTags, subspaces); - final processedTags = result['updatedTags'] as List; + final processedTags = + result['updatedTags'] as List; final processedSubspaces = - List.from(result['subspaces'] as List); + List.from( + result['subspaces'] as List); onSave?.call(processedTags, processedSubspaces); Navigator.of(context).pop(); } diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index 6343782c..57ed93df 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -78,6 +78,35 @@ class AssignTagModelsDialog extends StatelessWidget { AssignTagsTable( controllers: controllers, locations: locations, + tags: state.tags, + updatedTags: state.updatedTags, + onDeleteDevice: ({required index, required tag}) { + context + .read() + .add(DeleteTagModel( + tagToDelete: tag, + tags: state.tags, + )); + controllers.removeAt(index); + }, + onTagDropDownSelected: ( + {required index, required tag}) { + context.read().add( + UpdateTag( + index: index, + tag: tag, + ), + ); + }, + onLocationDropDownSelected: ( + {required index, required location}) { + context.read().add( + UpdateLocation( + index: index, + location: location, + ), + ); + }, ), if (state.errorMessage != null) Text(state.errorMessage!, diff --git a/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart b/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart index eaddc26c..0e17a0d7 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart @@ -1,172 +1,155 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:uuid/uuid.dart'; import '../../../../../common/dialog_dropdown.dart'; import '../../../../../common/tag_dialog_textfield_dropdown.dart'; import '../../../../../utils/color_manager.dart'; -import '../../bloc/assign_tag_model_bloc.dart'; -import '../../bloc/assign_tag_model_event.dart'; -import '../../bloc/assign_tag_model_state.dart'; class AssignTagsTable extends StatelessWidget { const AssignTagsTable({ super.key, required this.controllers, required this.locations, + required this.tags, + required this.updatedTags, + required this.onDeleteDevice, + required this.onLocationDropDownSelected, + required this.onTagDropDownSelected, }); - + final void Function({required Tag tag, required int index}) + onTagDropDownSelected; + final void Function({required String location, required int index}) + onLocationDropDownSelected; + final void Function({required Tag tag, required int index}) onDeleteDevice; + final List tags; + final List updatedTags; final List controllers; final List locations; @override Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is AssignTagModelLoaded) { - return ClipRRect( - borderRadius: BorderRadius.circular(20), - child: DataTable( - headingRowColor: - WidgetStateProperty.all(ColorsManager.dataHeaderGrey), - key: ValueKey(state.tags.length), - border: TableBorder.all( - color: ColorsManager.dataHeaderGrey, - width: 1, - borderRadius: BorderRadius.circular(20), - ), - columns: [ - DataColumn( - label: Text('#', - style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Device', - style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - numeric: false, - label: Text('Tag', - style: Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Location', - style: Theme.of(context).textTheme.bodyMedium)), - ], - rows: state.tags.isEmpty - ? [ - DataRow(cells: [ - DataCell( - Center( - child: Text('No Devices Available', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager.lightGrayColor, - )), - ), - ), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - const DataCell(SizedBox()), - ]) - ] - : List.generate(state.tags.length, (index) { - final tag = state.tags[index]; - final controller = controllers[index]; + return ClipRRect( + borderRadius: BorderRadius.circular(20), + child: DataTable( + headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), + key: ValueKey(tags.length), + border: TableBorder.all( + color: ColorsManager.dataHeaderGrey, + width: 1, + borderRadius: BorderRadius.circular(20), + ), + columns: [ + DataColumn( + label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + label: Text('Device', + style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + numeric: false, + label: + Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), + DataColumn( + label: Text('Location', + style: Theme.of(context).textTheme.bodyMedium)), + ], + rows: tags.isEmpty + ? [ + DataRow(cells: [ + DataCell( + Center( + child: Text('No Devices Available', + style: + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.lightGrayColor, + )), + ), + ), + const DataCell(SizedBox()), + const DataCell(SizedBox()), + const DataCell(SizedBox()), + ]) + ] + : List.generate(tags.length, (index) { + final tag = tags[index]; + final controller = controllers[index]; - return DataRow( - cells: [ - DataCell(Text((index + 1).toString())), - DataCell( - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - tag.product?.name ?? 'Unknown', - overflow: TextOverflow.ellipsis, - )), - const SizedBox(width: 10), - Container( - width: 20.0, - height: 20.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.0, - ), - ), - child: IconButton( - icon: const Icon( - Icons.close, - color: ColorsManager.lightGreyColor, - size: 16, - ), - onPressed: () { - context.read().add( - DeleteTagModel( - tagToDelete: tag, - tags: state.tags)); - controllers.removeAt(index); - }, - tooltip: 'Delete Tag', - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - ), - ), - ], - ), - ), - DataCell( - Container( - alignment: Alignment - .centerLeft, // Align cell content to the left - child: SizedBox( - width: double.infinity, - child: TagDialogTextfieldDropdown( - key: ValueKey( - 'dropdown_${const Uuid().v4()}_$index'), - product: tag.product?.uuid ?? 'Unknown', - items: state.updatedTags, - initialValue: tag, - onSelected: (value) { - controller.text = value.tag ?? ''; - context - .read() - .add(UpdateTag( - index: index, - tag: value, - )); - }, - ), + return DataRow( + cells: [ + DataCell(Text((index + 1).toString())), + DataCell( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + tag.product?.name ?? 'Unknown', + overflow: TextOverflow.ellipsis, + )), + const SizedBox(width: 10), + Container( + width: 20.0, + height: 20.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1.0, ), ), - ), - DataCell( - SizedBox( - width: double.infinity, - child: DialogDropdown( - items: locations, - selectedValue: tag.location ?? 'Main Space', - onSelected: (value) { - context - .read() - .add(UpdateLocation( - index: index, - location: value, - )); - }, - )), + child: IconButton( + icon: const Icon( + Icons.close, + color: ColorsManager.lightGreyColor, + size: 16, + ), + onPressed: () { + onDeleteDevice(tag: tag, index: index); + }, + tooltip: 'Delete Tag', + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), ), ], - ); - }), - ), - ); - } else { - return const SizedBox(); - } - }, + ), + ), + DataCell( + Container( + alignment: Alignment + .centerLeft, // Align cell content to the left + child: SizedBox( + width: double.infinity, + child: TagDialogTextfieldDropdown( + key: ValueKey( + 'dropdown_${const Uuid().v4()}_$index'), + product: tag.product?.uuid ?? 'Unknown', + items: updatedTags, + initialValue: tag, + onSelected: (value) { + controller.text = value.tag ?? ''; + onTagDropDownSelected(tag: value, index: index); + }, + ), + ), + ), + ), + DataCell( + SizedBox( + width: double.infinity, + child: DialogDropdown( + items: locations, + selectedValue: tag.location ?? 'Main Space', + onSelected: (value) { + onLocationDropDownSelected( + location: value, index: index); + }, + )), + ), + ], + ); + }), + ), ); } } From a87e79878b1e118a639c4374c2a03ee6f08a7bea Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Tue, 27 May 2025 05:32:42 -0500 Subject: [PATCH 10/41] add ok_candel row of buttons in seperated widget also text field widget --- .../views/create_subspace_model_dialog.dart | 149 ++---------------- .../widgets/ok_cancel_sub_space_widget.dart | 72 +++++++++ .../textfield_sub_space_dialog_widget.dart | 98 ++++++++++++ 3 files changed, 181 insertions(+), 138 deletions(-) create mode 100644 lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart create mode 100644 lib/pages/spaces_management/create_subspace/views/widgets/textfield_sub_space_dialog_widget.dart diff --git a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart index 483382af..501ad5a1 100644 --- a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart @@ -12,6 +12,9 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_ import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'widgets/ok_cancel_sub_space_widget.dart'; +import 'widgets/textfield_sub_space_dialog_widget.dart'; + class CreateSubSpaceDialog extends StatefulWidget { final String dialogTitle; final List? existingSubSpaces; @@ -33,14 +36,7 @@ class CreateSubSpaceDialog extends StatefulWidget { } class _CreateSubSpaceDialogState extends State { - late final TextEditingController _subspaceNameController; - - @override - void initState() { - _subspaceNameController = TextEditingController(); - super.initState(); - } - + final TextEditingController _subspaceNameController = TextEditingController(); @override void dispose() { _subspaceNameController.dispose(); @@ -96,84 +92,9 @@ class _CreateSubSpaceDialogState extends State { ], ), const SizedBox(height: 16), - Container( - width: context.screenWidth * 0.35, - padding: const EdgeInsets.symmetric( - vertical: 10, - horizontal: 16, - ), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(10), - ), - child: Wrap( - spacing: 8, - runSpacing: 8, - alignment: WrapAlignment.start, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - ...state.subSpaces.asMap().entries.map( - (entry) { - final index = entry.key; - final subSpace = entry.value; - - final lowerName = - subSpace.subspaceName.toLowerCase(); - - final duplicateIndices = state.subSpaces - .asMap() - .entries - .where((e) => - e.value.subspaceName.toLowerCase() == - lowerName) - .map((e) => e.key) - .toList(); - final isDuplicate = duplicateIndices.length > 1 && - duplicateIndices.indexOf(index) != 0; - return SubspaceChip( - subSpace: SubspaceTemplateModel( - subspaceName: entry.value.subspaceName, - disabled: entry.value.disabled, - ), - isDuplicate: isDuplicate, - onDeleted: () => context.read().add( - RemoveSubSpace(subSpace), - ), - ); - }, - ), - SizedBox( - width: 200, - child: TextField( - controller: _subspaceNameController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: state.subSpaces.isEmpty - ? 'Please enter the name' - : null, - hintStyle: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.lightGrayColor, - ), - ), - onSubmitted: (value) { - final trimmedValue = value.trim(); - if (trimmedValue.isNotEmpty) { - context.read().add( - AddSubSpace( - SubspaceModel( - subspaceName: trimmedValue, - disabled: false, - ), - ), - ); - _subspaceNameController.clear(); - } - }, - style: context.textTheme.bodyMedium, - ), - ), - ], - ), + TextFieldSubSpaceDialogWidget( + subspaceNameController: _subspaceNameController, + subSpaces: state.subSpaces, ), if (state.errorMessage.isNotEmpty) Padding( @@ -186,58 +107,10 @@ class _CreateSubSpaceDialogState extends State { ), ), const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: CancelButton( - label: 'Cancel', - onPressed: () async { - Navigator.of(context).pop(); - }, - ), - ), - const SizedBox(width: 10), - Expanded( - child: DefaultButton( - onPressed: state.errorMessage.isEmpty - ? () async { - final trimmedValue = - _subspaceNameController.text.trim(); - - final subSpacesBloc = - context.read(); - if (trimmedValue.isNotEmpty) { - subSpacesBloc.add( - AddSubSpace( - SubspaceModel( - subspaceName: trimmedValue, - disabled: false, - ), - ), - ); - _subspaceNameController.clear(); - } - - await Future.delayed( - const Duration(milliseconds: 10)); - final subSpaces = - subSpacesBloc.state.subSpaces; - // if (subSpaces.isNotEmpty) { - widget.onSave?.call(subSpaces); - // } - - Navigator.of(context).pop(); - } - : null, - backgroundColor: ColorsManager.secondaryColor, - borderRadius: 10, - foregroundColor: state.errorMessage.isNotEmpty - ? ColorsManager.whiteColorsWithOpacity - : ColorsManager.whiteColors, - child: const Text('OK'), - ), - ), - ], + OkCancelSubSpaceWidget( + subspaceNameController: _subspaceNameController, + errorMessage: state.errorMessage, + onSave: widget.onSave, ), ], ), diff --git a/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart b/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart new file mode 100644 index 00000000..3c3db8f0 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; + +import '../../bloc/subspace_bloc.dart'; +import '../../bloc/subspace_event.dart'; + +class OkCancelSubSpaceWidget extends StatelessWidget { + const OkCancelSubSpaceWidget({ + super.key, + required this.subspaceNameController, + required this.onSave, + required this.errorMessage, + }); + + final TextEditingController subspaceNameController; + final void Function(List?)? onSave; + final String errorMessage; + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + child: CancelButton( + label: 'Cancel', + onPressed: () async { + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + onPressed: errorMessage.isEmpty + ? () async { + final trimmedValue = subspaceNameController.text.trim(); + + final subSpacesBloc = context.read(); + if (trimmedValue.isNotEmpty) { + subSpacesBloc.add( + AddSubSpace( + SubspaceModel( + subspaceName: trimmedValue, + disabled: false, + ), + ), + ); + subspaceNameController.clear(); + } + + await Future.delayed(const Duration(milliseconds: 10)); + final subSpaces = subSpacesBloc.state.subSpaces; + onSave?.call(subSpaces); + + Navigator.of(context).pop(); + } + : null, + backgroundColor: ColorsManager.secondaryColor, + borderRadius: 10, + foregroundColor: errorMessage.isNotEmpty + ? ColorsManager.whiteColorsWithOpacity + : ColorsManager.whiteColors, + child: const Text('OK'), + ), + ), + ], + ); + } +} diff --git a/lib/pages/spaces_management/create_subspace/views/widgets/textfield_sub_space_dialog_widget.dart b/lib/pages/spaces_management/create_subspace/views/widgets/textfield_sub_space_dialog_widget.dart new file mode 100644 index 00000000..f655e178 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/views/widgets/textfield_sub_space_dialog_widget.dart @@ -0,0 +1,98 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +import '../../../../../utils/color_manager.dart'; +import '../../../all_spaces/model/subspace_model.dart'; +import '../../../create_subspace_model/widgets/subspace_chip.dart'; +import '../../../space_model/models/subspace_template_model.dart'; +import '../../bloc/subspace_bloc.dart'; +import '../../bloc/subspace_event.dart'; + +class TextFieldSubSpaceDialogWidget extends StatelessWidget { + const TextFieldSubSpaceDialogWidget({ + super.key, + required TextEditingController subspaceNameController, + required this.subSpaces, + }) : _subspaceNameController = subspaceNameController; + + final TextEditingController _subspaceNameController; + final List subSpaces; + @override + Widget build(BuildContext context) { + return Container( + width: context.screenWidth * 0.35, + padding: const EdgeInsets.symmetric( + vertical: 10, + horizontal: 16, + ), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(10), + ), + child: Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + ...subSpaces.asMap().entries.map( + (entry) { + final index = entry.key; + final subSpace = entry.value; + + final lowerName = subSpace.subspaceName.toLowerCase(); + + final duplicateIndices = subSpaces + .asMap() + .entries + .where((e) => e.value.subspaceName.toLowerCase() == lowerName) + .map((e) => e.key) + .toList(); + final isDuplicate = duplicateIndices.length > 1 && + duplicateIndices.indexOf(index) != 0; + return SubspaceChip( + subSpace: SubspaceTemplateModel( + subspaceName: entry.value.subspaceName, + disabled: entry.value.disabled, + ), + isDuplicate: isDuplicate, + onDeleted: () => context.read().add( + RemoveSubSpace(subSpace), + ), + ); + }, + ), + SizedBox( + width: 200, + child: TextField( + controller: _subspaceNameController, + decoration: InputDecoration( + border: InputBorder.none, + hintText: subSpaces.isEmpty ? 'Please enter the name' : null, + hintStyle: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.lightGrayColor, + ), + ), + onSubmitted: (value) { + final trimmedValue = value.trim(); + if (trimmedValue.isNotEmpty) { + context.read().add( + AddSubSpace( + SubspaceModel( + subspaceName: trimmedValue, + disabled: false, + ), + ), + ); + _subspaceNameController.clear(); + } + }, + style: context.textTheme.bodyMedium, + ), + ), + ], + ), + ); + } +} From 8967852ca85bd01bf2cabf5a11458684a211d769 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Tue, 27 May 2025 06:01:25 -0500 Subject: [PATCH 11/41] seperate assign tag dialog to widgets --- .../assign_tag/views/assign_tag_dialog.dart | 91 +++------------- .../widgets/save_add_device_row_widget.dart | 100 ++++++++++++++++++ 2 files changed, 113 insertions(+), 78 deletions(-) create mode 100644 lib/pages/spaces_management/assign_tag/views/widgets/save_add_device_row_widget.dart diff --git a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart index a244d235..21ba141c 100644 --- a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart +++ b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart @@ -1,10 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.dart'; -import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; -import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; @@ -13,9 +8,9 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_b import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:uuid/uuid.dart'; + +import 'widgets/save_add_device_row_widget.dart'; class AssignTagDialog extends StatelessWidget { final List? products; @@ -30,7 +25,7 @@ class AssignTagDialog extends StatelessWidget { final List projectTags; const AssignTagDialog( - {Key? key, + {super.key, required this.products, required this.subspaces, required this.addedProducts, @@ -40,8 +35,7 @@ class AssignTagDialog extends StatelessWidget { required this.spaceName, required this.title, this.onSave, - required this.projectTags}) - : super(key: key); + required this.projectTags}); @override Widget build(BuildContext context) { @@ -106,74 +100,15 @@ class AssignTagDialog extends StatelessWidget { ), ), actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const SizedBox(width: 10), - Expanded( - child: Builder( - builder: (buttonContext) => CancelButton( - label: 'Add New Device', - onPressed: () async { - final updatedTags = List.from(state.tags); - final result = - TagHelper.processTags(updatedTags, subspaces); - - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = List.from( - result['subspaces'] as List); - - Navigator.of(context).pop(); - - await showDialog( - context: context, - builder: (context) => AddDeviceTypeWidget( - products: products, - subspaces: processedSubspaces, - projectTags: projectTags, - initialSelectedProducts: TagHelper - .createInitialSelectedProductsForTags( - processedTags, processedSubspaces), - spaceName: spaceName, - spaceTags: processedTags, - isCreate: false, - onSave: onSave, - allTags: allTags, - ), - ); - }, - ), - ), - ), - const SizedBox(width: 10), - Expanded( - child: DefaultButton( - borderRadius: 10, - backgroundColor: ColorsManager.secondaryColor, - foregroundColor: state.isSaveEnabled - ? ColorsManager.whiteColors - : ColorsManager.whiteColorsWithOpacity, - onPressed: state.isSaveEnabled - ? () async { - final updatedTags = List.from(state.tags); - final result = TagHelper.processTags( - updatedTags, subspaces); - - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = - List.from( - result['subspaces'] as List); - onSave?.call(processedTags, processedSubspaces); - Navigator.of(context).pop(); - } - : null, - child: const Text('Save'), - ), - ), - const SizedBox(width: 10), - ], + SaveAddDeviceRowWidget( + subspaces: subspaces, + products: products, + projectTags: projectTags, + spaceName: spaceName, + onSave: onSave, + allTags: allTags, + tags: state.tags, + isSaveEnabled: state.isSaveEnabled, ), ], ); diff --git a/lib/pages/spaces_management/assign_tag/views/widgets/save_add_device_row_widget.dart b/lib/pages/spaces_management/assign_tag/views/widgets/save_add_device_row_widget.dart new file mode 100644 index 00000000..a81b7ad0 --- /dev/null +++ b/lib/pages/spaces_management/assign_tag/views/widgets/save_add_device_row_widget.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +import '../../../add_device_type/views/add_device_type_widget.dart'; +import '../../../helper/tag_helper.dart'; + +class SaveAddDeviceRowWidget extends StatelessWidget { + const SaveAddDeviceRowWidget({ + super.key, + required this.subspaces, + required this.products, + required this.projectTags, + required this.spaceName, + required this.onSave, + required this.allTags, + required this.tags, + required this.isSaveEnabled, + }); + final List tags; + final List? subspaces; + final List? products; + final List projectTags; + final String spaceName; + final Function(List p1, List? p2)? onSave; + final List? allTags; + final bool isSaveEnabled; + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const SizedBox(width: 10), + Expanded( + child: Builder( + builder: (buttonContext) => CancelButton( + label: 'Add New Device', + onPressed: () async { + final updatedTags = List.from(tags); + final result = TagHelper.processTags(updatedTags, subspaces); + + final processedTags = result['updatedTags'] as List; + final processedSubspaces = List.from( + result['subspaces'] as List); + + Navigator.of(context).pop(); + + await showDialog( + context: context, + builder: (context) => AddDeviceTypeWidget( + products: products, + subspaces: processedSubspaces, + projectTags: projectTags, + initialSelectedProducts: + TagHelper.createInitialSelectedProductsForTags( + processedTags, processedSubspaces), + spaceName: spaceName, + spaceTags: processedTags, + isCreate: false, + onSave: onSave, + allTags: allTags, + ), + ); + }, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + borderRadius: 10, + backgroundColor: ColorsManager.secondaryColor, + foregroundColor: isSaveEnabled + ? ColorsManager.whiteColors + : ColorsManager.whiteColorsWithOpacity, + onPressed: isSaveEnabled + ? () async { + final updatedTags = List.from(tags); + final result = + TagHelper.processTags(updatedTags, subspaces); + + final processedTags = result['updatedTags'] as List; + final processedSubspaces = List.from( + result['subspaces'] as List); + onSave?.call(processedTags, processedSubspaces); + Navigator.of(context).pop(); + } + : null, + child: const Text('Save'), + ), + ), + const SizedBox(width: 10), + ], + ); + } +} From fc81555be3182b558b6bcb4c815d12693e976184 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 00:40:34 -0500 Subject: [PATCH 12/41] seperate iconchoose widget in create space dialog --- .../icon_choose_part_widget.dart | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart new file mode 100644 index 00000000..59a100a3 --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../../utils/color_manager.dart'; +import '../../../../../utils/constants/assets.dart'; + +class IconChoosePartWidget extends StatelessWidget { + const IconChoosePartWidget({ + super.key, + required this.selectedIcon, + required this.showIconSelection, + required this.screenWidth, + }); + final double screenWidth; + final String selectedIcon; + final void Function() showIconSelection; + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 50), + Stack( + alignment: Alignment.center, + children: [ + Container( + width: screenWidth * 0.1, + height: screenWidth * 0.1, + decoration: const BoxDecoration( + color: ColorsManager.boxColor, + shape: BoxShape.circle, + ), + ), + SvgPicture.asset( + selectedIcon, + width: screenWidth * 0.04, + height: screenWidth * 0.04, + ), + Positioned( + top: 20, + right: 20, + child: InkWell( + onTap: showIconSelection, + child: Container( + width: 24, + height: 24, + decoration: const BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: SvgPicture.asset( + Assets.iconEdit, + width: 16, + height: 16, + ), + ), + ), + ), + ], + ), + ], + ); + } +} From 52046909d5598bebef85b9c1ea591d58b4a92d27 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 00:41:01 -0500 Subject: [PATCH 13/41] seperate textfield of the space name --- .../space_name_textfield_widget.dart | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart new file mode 100644 index 00000000..d9abbf7f --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +import '../../../../../utils/color_manager.dart'; + +class SpaceNameTextfieldWidget extends StatelessWidget { + SpaceNameTextfieldWidget({ + super.key, + required this.isNameFieldExist, + required this.isNameFieldInvalid, + required this.onChange, + required this.screenWidth, + }); + TextEditingController nameController = TextEditingController(); + final void Function(String value) onChange; + final double screenWidth; + bool isNameFieldExist; + bool isNameFieldInvalid; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + SizedBox( + width: screenWidth * 0.25, + child: TextField( + controller: nameController, + onChanged: onChange, + style: Theme.of(context).textTheme.bodyMedium, + decoration: InputDecoration( + hintText: 'Please enter the name', + hintStyle: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: ColorsManager.lightGrayColor), + filled: true, + fillColor: ColorsManager.boxColor, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide( + color: isNameFieldInvalid || isNameFieldExist + ? ColorsManager.red + : ColorsManager.boxColor, + width: 1.5, + ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: ColorsManager.boxColor, + width: 1.5, + ), + ), + ), + ), + ), + 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), + ), + ), + 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), + ), + ), + ], + ); + } +} From 4feae9ad87920d7095222dfea01790d889b8589f Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 00:41:33 -0500 Subject: [PATCH 14/41] seperate spacemodel linking into widget --- .../space_model_linking_widget.dart | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart new file mode 100644 index 00000000..cd9ae470 --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import '../../../../../utils/color_manager.dart'; +import '../../../../../utils/constants/assets.dart'; +import '../../../space_model/models/space_template_model.dart'; +import '../../../space_model/widgets/button_content_widget.dart'; + +class SpaceModelLinkingWidget extends StatelessWidget { + const SpaceModelLinkingWidget({ + super.key, + required this.onDeleted, + required this.onPressed, + required this.screenWidth, + required this.selectedSpaceModel, + required this.isSpaceModelDisabled, + }); + final bool isSpaceModelDisabled; + final void Function()? onDeleted; + final void Function()? onPressed; + final double screenWidth; + final SpaceTemplateModel? selectedSpaceModel; + @override + Widget build(BuildContext context) { + return Column( + children: [ + selectedSpaceModel == null + ? TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: onPressed, + child: ButtonContentWidget( + svgAssets: Assets.link, + label: 'Link a space model', + disabled: isSpaceModelDisabled, + ), + ) + : Container( + width: screenWidth * 0.25, + padding: const EdgeInsets.symmetric( + vertical: 10.0, horizontal: 16.0), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(10), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: [ + Chip( + label: Text( + selectedSpaceModel?.modelName ?? '', + style: Theme.of(context) + .textTheme + .bodyMedium! + .copyWith(color: ColorsManager.spaceColor), + ), + backgroundColor: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: const BorderSide( + color: ColorsManager.transparentColor, + width: 0, + ), + ), + deleteIcon: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1.5, + ), + ), + child: const Icon( + Icons.close, + size: 16, + color: ColorsManager.lightGrayColor, + ), + ), + onDeleted: onDeleted), + ], + ), + ), + ], + ); + } +} From 6ec20e2d72afc382135e7e8b3335766d343a9047 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 00:41:53 -0500 Subject: [PATCH 15/41] seperate subspace part into widget --- .../sub_space_part_widget.dart | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart new file mode 100644 index 00000000..e518877a --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; + +import '../../../../../common/edit_chip.dart'; +import '../../../../../utils/color_manager.dart'; +import '../../../space_model/widgets/button_content_widget.dart'; +import '../../../space_model/widgets/subspace_name_label_widget.dart'; +import '../../model/subspace_model.dart'; + +class SubSpacePartWidget extends StatefulWidget { + SubSpacePartWidget({ + super.key, + required this.subspaces, + required this.onPressed, + required this.isTagsAndSubspaceModelDisabled, + required this.screenWidth, + required this.editChipOnTap, + }); + double screenWidth; + bool isTagsAndSubspaceModelDisabled; + final void Function() editChipOnTap; + List? subspaces; + final void Function() onPressed; + + @override + State createState() => _SubSpacePartWidgetState(); +} + +class _SubSpacePartWidgetState extends State { + @override + Widget build(BuildContext context) { + return Column( + children: [ + widget.subspaces == null || widget.subspaces!.isEmpty + ? TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + overlayColor: ColorsManager.transparentColor, + ), + onPressed: widget.onPressed, + child: ButtonContentWidget( + icon: Icons.add, + label: 'Create Sub Spaces', + disabled: widget.isTagsAndSubspaceModelDisabled, + ), + ) + : SizedBox( + width: widget.screenWidth * 0.25, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: ColorsManager.textFieldGreyColor, + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: ColorsManager.textFieldGreyColor, + width: 3.0, // Border width + ), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: [ + if (widget.subspaces != null) + ...widget.subspaces!.map((subspace) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SubspaceNameDisplayWidget( + text: subspace.subspaceName, + validateName: (updatedName) { + bool nameExists = widget.subspaces!.any((s) { + bool isSameId = + s.internalId == subspace.internalId; + bool isSameName = + s.subspaceName.trim().toLowerCase() == + updatedName.trim().toLowerCase(); + + return !isSameId && isSameName; + }); + + return !nameExists; + }, + onNameChanged: (updatedName) { + setState(() { + subspace.subspaceName = updatedName; + }); + }, + ), + ], + ); + }), + EditChip( + onTap: widget.editChipOnTap, + ) + ], + ), + ), + ) + ], + ); + } +} From 2c73dd6c31557d4058348c40092f3d82e68d607d Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 00:42:05 -0500 Subject: [PATCH 16/41] call the new widgets --- .../widgets/dialogs/create_space_dialog.dart | 322 ++++-------------- 1 file changed, 60 insertions(+), 262 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index b3d1e358..5223a091 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -9,6 +9,10 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_pr import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart'; @@ -116,50 +120,10 @@ class CreateSpaceDialogState extends State { children: [ Expanded( flex: 1, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - // crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox(height: 50), - Stack( - alignment: Alignment.center, - children: [ - Container( - width: screenWidth * 0.1, - height: screenWidth * 0.1, - decoration: const BoxDecoration( - color: ColorsManager.boxColor, - shape: BoxShape.circle, - ), - ), - SvgPicture.asset( - selectedIcon, - width: screenWidth * 0.04, - height: screenWidth * 0.04, - ), - Positioned( - top: 20, - right: 20, - child: InkWell( - onTap: _showIconSelectionDialog, - child: Container( - width: 24, - height: 24, - decoration: const BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - child: SvgPicture.asset( - Assets.iconEdit, - width: 16, - height: 16, - ), - ), - ), - ), - ], - ), - ], + child: IconChoosePartWidget( + selectedIcon: selectedIcon, + showIconSelection: _showIconSelectionDialog, + screenWidth: screenWidth, ), ), const SizedBox(width: 20), @@ -168,149 +132,46 @@ class CreateSpaceDialogState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox( - width: screenWidth * 0.25, - child: TextField( - controller: nameController, - onChanged: (value) { - enteredName = value.trim(); - setState(() { - isNameFieldExist = false; - isOkButtonEnabled = false; - isNameFieldInvalid = value.isEmpty; + SpaceNameTextfieldWidget( + isNameFieldExist: isNameFieldExist, + isNameFieldInvalid: isNameFieldInvalid, + screenWidth: screenWidth, + onChange: (value) { + enteredName = value.trim(); + setState(() { + isNameFieldExist = false; + isOkButtonEnabled = false; + isNameFieldInvalid = value.isEmpty; - if (!isNameFieldInvalid) { - if (SpaceHelper.isNameConflict(value, - widget.parentSpace, widget.editSpace)) { - isNameFieldExist = true; - isOkButtonEnabled = false; - } else { - isNameFieldExist = false; - isOkButtonEnabled = true; - } + if (!isNameFieldInvalid) { + if (SpaceHelper.isNameConflict( + value, widget.parentSpace, widget.editSpace)) { + isNameFieldExist = true; + isOkButtonEnabled = false; + } else { + isNameFieldExist = false; + isOkButtonEnabled = true; } - }); - }, - style: Theme.of(context).textTheme.bodyMedium, - decoration: InputDecoration( - hintText: 'Please enter the name', - hintStyle: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith(color: ColorsManager.lightGrayColor), - filled: true, - fillColor: ColorsManager.boxColor, - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide( - color: isNameFieldInvalid || isNameFieldExist - ? ColorsManager.red - : ColorsManager.boxColor, - width: 1.5, - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: ColorsManager.boxColor, - width: 1.5, - ), - ), - ), - ), + } + }); + }, ), - 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), - ), - ), - 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: 10), - selectedSpaceModel == null - ? TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () { - isSpaceModelDisabled - ? null - : _showLinkSpaceModelDialog(context); - }, - child: ButtonContentWidget( - svgAssets: Assets.link, - label: 'Link a space model', - disabled: isSpaceModelDisabled, - ), - ) - : Container( - width: screenWidth * 0.25, - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 16.0), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(10), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 8.0, - children: [ - Chip( - label: Text( - selectedSpaceModel?.modelName ?? '', - style: Theme.of(context) - .textTheme - .bodyMedium! - .copyWith( - color: ColorsManager.spaceColor), - ), - backgroundColor: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: const BorderSide( - color: ColorsManager.transparentColor, - width: 0, - ), - ), - deleteIcon: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.5, - ), - ), - child: const Icon( - Icons.close, - size: 16, - color: ColorsManager.lightGrayColor, - ), - ), - onDeleted: () => setState(() { - this.selectedSpaceModel = null; - subspaces = widget.subspaces ?? []; - tags = widget.tags ?? []; - })), - ], - ), - ), + SpaceModelLinkingWidget( + isSpaceModelDisabled: isSpaceModelDisabled, + onPressed: () { + isSpaceModelDisabled + ? null + : _showLinkSpaceModelDialog(context); + }, + onDeleted: () => setState(() { + selectedSpaceModel = null; + subspaces = widget.subspaces ?? []; + tags = widget.tags ?? []; + }), + screenWidth: screenWidth, + selectedSpaceModel: selectedSpaceModel, + ), const SizedBox(height: 25), Row( children: [ @@ -339,85 +200,22 @@ class CreateSpaceDialogState extends State { ], ), const SizedBox(height: 25), - subspaces == null || subspaces!.isEmpty - ? TextButton( - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - overlayColor: ColorsManager.transparentColor, - ), - onPressed: () async { - isTagsAndSubspaceModelDisabled - ? null - : _showSubSpaceDialog(context, enteredName, - [], false, widget.products, subspaces); - }, - child: ButtonContentWidget( - icon: Icons.add, - label: 'Create Sub Spaces', - disabled: isTagsAndSubspaceModelDisabled, - ), - ) - : SizedBox( - width: screenWidth * 0.25, - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: ColorsManager.textFieldGreyColor, - borderRadius: BorderRadius.circular(15), - border: Border.all( - color: ColorsManager.textFieldGreyColor, - width: 3.0, // Border width - ), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 8.0, - children: [ - if (subspaces != null) - ...subspaces!.map((subspace) { - return Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - SubspaceNameDisplayWidget( - text: subspace.subspaceName, - validateName: (updatedName) { - bool nameExists = - subspaces!.any((s) { - bool isSameId = s.internalId == - subspace.internalId; - bool isSameName = s.subspaceName - .trim() - .toLowerCase() == - updatedName - .trim() - .toLowerCase(); - - return !isSameId && isSameName; - }); - - return !nameExists; - }, - onNameChanged: (updatedName) { - setState(() { - subspace.subspaceName = - updatedName; - }); - }, - ), - ], - ); - }), - EditChip( - onTap: () async { - _showSubSpaceDialog(context, enteredName, - [], true, widget.products, subspaces); - }, - ) - ], - ), - ), - ), + SubSpacePartWidget( + subspaces: subspaces, + onPressed: () { + isTagsAndSubspaceModelDisabled + ? null + : _showSubSpaceDialog(context, enteredName, [], + false, widget.products, subspaces); + }, + isTagsAndSubspaceModelDisabled: + isTagsAndSubspaceModelDisabled, + screenWidth: screenWidth, + editChipOnTap: () async { + _showSubSpaceDialog(context, enteredName, [], true, + widget.products, subspaces); + }, + ), const SizedBox(height: 10), (tags?.isNotEmpty == true || subspaces?.any((subspace) => From cf9bafef4d84c12eebe31dd24c45ba63077d7c0b Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 01:20:31 -0500 Subject: [PATCH 17/41] add devices part widget --- .../devices_part_widget.dart | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart new file mode 100644 index 00000000..94896554 --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +import '../../../../../common/edit_chip.dart'; +import '../../../../../utils/color_manager.dart'; +import '../../../helper/tag_helper.dart'; +import '../../../space_model/widgets/button_content_widget.dart'; +import '../../model/subspace_model.dart'; +import '../../model/tag.dart'; + +class DevicesPartWidget extends StatelessWidget { + const DevicesPartWidget({ + super.key, + required this.tags, + required this.subspaces, + required this.screenWidth, + required this.onEditChip, + required this.onTextButtonPressed, + required this.isTagsAndSubspaceModelDisabled, + }); + final bool isTagsAndSubspaceModelDisabled; + final void Function() onEditChip; + final void Function() onTextButtonPressed; + final double screenWidth; + final List? tags; + final List? subspaces; + @override + Widget build(BuildContext context) { + return Column( + children: [ + (tags?.isNotEmpty == true || + subspaces?.any( + (subspace) => subspace.tags?.isNotEmpty == true) == + true) + ? SizedBox( + width: screenWidth * 0.25, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: ColorsManager.textFieldGreyColor, + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: ColorsManager.textFieldGreyColor, + width: 3.0, // Border width + ), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: [ + // Combine tags from spaceModel and subspaces + ...TagHelper.groupTags([ + ...?tags, + ...?subspaces?.expand((subspace) => subspace.tags ?? []) + ]).entries.map( + (entry) => Chip( + avatar: SizedBox( + width: 24, + height: 24, + child: SvgPicture.asset( + entry.key.icon ?? 'assets/icons/gateway.svg', + fit: BoxFit.contain, + ), + ), + label: Text( + 'x${entry.value}', // Show count + style: Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: ColorsManager.spaceColor), + ), + backgroundColor: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: const BorderSide( + color: ColorsManager.spaceColor, + ), + ), + ), + ), + + EditChip(onTap: onEditChip) + ], + ), + ), + ) + : TextButton( + onPressed: onTextButtonPressed, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + child: ButtonContentWidget( + icon: Icons.add, + label: 'Add Devices', + disabled: isTagsAndSubspaceModelDisabled, + ), + ) + ], + ); + } +} From 1db069e9a518a9343a2f9b3b61ae8b35f4967fa4 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 01:21:06 -0500 Subject: [PATCH 18/41] use devicePart in main widget --- .../widgets/dialogs/create_space_dialog.dart | 152 +++++------------- 1 file changed, 42 insertions(+), 110 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index 5223a091..0cbecfd8 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/common/edit_chip.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart'; @@ -9,6 +7,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_pr import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart'; @@ -20,8 +19,6 @@ import 'package:syncrow_web/pages/spaces_management/helper/space_helper.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.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'; @@ -217,113 +214,47 @@ class CreateSpaceDialogState extends State { }, ), const SizedBox(height: 10), - (tags?.isNotEmpty == true || - subspaces?.any((subspace) => - subspace.tags?.isNotEmpty == true) == - true) - ? SizedBox( - width: screenWidth * 0.25, - child: Container( - padding: const EdgeInsets.all(8.0), - decoration: BoxDecoration( - color: ColorsManager.textFieldGreyColor, - borderRadius: BorderRadius.circular(15), - border: Border.all( - color: ColorsManager.textFieldGreyColor, - width: 3.0, // Border width - ), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 8.0, - children: [ - // Combine tags from spaceModel and subspaces - ...TagHelper.groupTags([ - ...?tags, - ...?subspaces?.expand( - (subspace) => subspace.tags ?? []) - ]).entries.map( - (entry) => Chip( - avatar: SizedBox( - width: 24, - height: 24, - child: SvgPicture.asset( - entry.key.icon ?? - 'assets/icons/gateway.svg', - fit: BoxFit.contain, - ), - ), - label: Text( - 'x${entry.value}', // Show count - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith( - color: ColorsManager - .spaceColor), - ), - backgroundColor: - ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(16), - side: const BorderSide( - color: ColorsManager.spaceColor, - ), - ), - ), - ), - - EditChip(onTap: () async { - await showDialog( - context: context, - builder: (context) => AssignTagDialog( - products: widget.products, - subspaces: subspaces, - allTags: widget.allTags, - addedProducts: TagHelper - .createInitialSelectedProductsForTags( - tags ?? [], subspaces), - title: 'Edit Device', - initialTags: - TagHelper.generateInitialForTags( - spaceTags: tags, - subspaces: subspaces), - spaceName: widget.name ?? '', - projectTags: widget.projectTags, - onSave: - (updatedTags, updatedSubspaces) { - setState(() { - tags = updatedTags; - subspaces = updatedSubspaces; - }); - }, - ), - ); - }) - ], - ), - ), - ) - : TextButton( - onPressed: () { - isTagsAndSubspaceModelDisabled - ? null - : _showTagCreateDialog( - context, - enteredName, - widget.isEdit, - widget.products, - ); + DevicesPartWidget( + tags: tags, + subspaces: subspaces, + screenWidth: screenWidth, + isTagsAndSubspaceModelDisabled: + isTagsAndSubspaceModelDisabled, + onEditChip: () async { + await showDialog( + context: context, + builder: (context) => AssignTagDialog( + products: widget.products, + subspaces: subspaces, + allTags: widget.allTags, + addedProducts: + TagHelper.createInitialSelectedProductsForTags( + tags ?? [], subspaces), + title: 'Edit Device', + initialTags: TagHelper.generateInitialForTags( + spaceTags: tags, subspaces: subspaces), + spaceName: widget.name ?? '', + projectTags: widget.projectTags, + onSave: (updatedTags, updatedSubspaces) { + setState(() { + tags = updatedTags; + subspaces = updatedSubspaces; + }); }, - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), - child: ButtonContentWidget( - icon: Icons.add, - label: 'Add Devices', - disabled: isTagsAndSubspaceModelDisabled, - )) + ), + ); + }, + onTextButtonPressed: () { + isTagsAndSubspaceModelDisabled + ? null + : _showTagCreateDialog( + context, + enteredName, + widget.isEdit, + widget.products, + ); + }, + ) ], ), ), @@ -382,6 +313,7 @@ class CreateSpaceDialogState extends State { ); } +//dialooogggs void _showIconSelectionDialog() { showDialog( context: context, From ee244fa5ed5dc77eb941c3743e524984d7dc5c4a Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 01:48:37 -0500 Subject: [PATCH 19/41] use textfield controller from constructor --- .../create_space_widgets/space_name_textfield_widget.dart | 3 ++- .../all_spaces/widgets/dialogs/create_space_dialog.dart | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart index d9abbf7f..600cb8ad 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart @@ -9,8 +9,9 @@ class SpaceNameTextfieldWidget extends StatelessWidget { required this.isNameFieldInvalid, required this.onChange, required this.screenWidth, + required this.nameController, }); - TextEditingController nameController = TextEditingController(); + TextEditingController nameController; final void Function(String value) onChange; final double screenWidth; bool isNameFieldExist; diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index 0cbecfd8..eaadf671 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -132,6 +132,7 @@ class CreateSpaceDialogState extends State { SpaceNameTextfieldWidget( isNameFieldExist: isNameFieldExist, isNameFieldInvalid: isNameFieldInvalid, + nameController: nameController, screenWidth: screenWidth, onChange: (value) { enteredName = value.trim(); From 321df401fd4ede301d921d577c99bf43e65f8171 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 01:56:57 -0500 Subject: [PATCH 20/41] comment interceptor cuz it is crashing app --- lib/services/api/http_service.dart | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/services/api/http_service.dart b/lib/services/api/http_service.dart index af5b60fe..c76291bf 100644 --- a/lib/services/api/http_service.dart +++ b/lib/services/api/http_service.dart @@ -23,17 +23,17 @@ class HTTPService { client.interceptors.add(serviceLocator.get()); // Add this interceptor for logging requests and responses - client.interceptors.add( - LogInterceptor( - request: true, - requestHeader: true, - requestBody: true, - responseHeader: false, - responseBody: true, - error: true, - logPrint: (object) => print(object), - ), - ); + // client.interceptors.add( + // LogInterceptor( + // request: true, + // requestHeader: true, + // requestBody: true, + // responseHeader: false, + // responseBody: true, + // error: true, + // logPrint: (object) => print(object), + // ), + // ); return client; } From c99b32fb81e5c0ec5bec89b948b49c5b43f4f9a1 Mon Sep 17 00:00:00 2001 From: Rafeek Alkhoudare Date: Wed, 28 May 2025 06:50:04 -0500 Subject: [PATCH 21/41] cancel direction --- .../bloc/space_management_bloc.dart | 114 +++++++++++------- .../all_spaces/model/connection_model.dart | 17 +-- .../all_spaces/model/space_model.dart | 2 +- .../widgets/community_structure_widget.dart | 11 +- .../widgets/curved_line_painter.dart | 27 +---- .../widgets/plus_button_widget.dart | 25 ++-- .../all_spaces/widgets/space_card_widget.dart | 2 +- lib/services/space_mana_api.dart | 8 +- 8 files changed, 100 insertions(+), 106 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart index 759cea27..04087257 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart @@ -21,7 +21,8 @@ import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action; -class SpaceManagementBloc extends Bloc { +class SpaceManagementBloc + extends Bloc { final CommunitySpaceManagementApi _api; final ProductApi _productApi; final SpaceModelManagementApi _spaceModelApi; @@ -62,7 +63,8 @@ class SpaceManagementBloc extends Bloc emit) async { + void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event, + Emitter emit) async { if (_cachedSpaceModels != null) { - _cachedSpaceModels = - _cachedSpaceModels!.where((model) => model.uuid != event.deletedUuid).toList(); + _cachedSpaceModels = _cachedSpaceModels! + .where((model) => model.uuid != event.deletedUuid) + .toList(); } else { _cachedSpaceModels = await fetchSpaceModels(); } await fetchTags(); emit(SpaceModelLoaded( - communities: - state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [], + communities: state is SpaceManagementLoaded + ? (state as SpaceManagementLoaded).communities + : [], products: _cachedProducts ?? [], spaceModels: List.from(_cachedSpaceModels ?? []), allTags: _cachedTags ?? [])); @@ -122,8 +127,8 @@ class SpaceManagementBloc extends Bloc.from(previousState.communities); + final updatedCommunities = + List.from(previousState.communities); for (var community in updatedCommunities) { if (community.uuid == event.communityUuid) { community.name = event.name; @@ -212,7 +219,8 @@ class SpaceManagementBloc extends Bloc> _fetchSpacesForCommunity(String communityUuid) async { + Future> _fetchSpacesForCommunity( + String communityUuid) async { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; return await _api.getSpaceHierarchy(communityUuid, projectUuid); @@ -242,20 +250,23 @@ class SpaceManagementBloc extends Bloc _onBlankState(BlankStateEvent event, Emitter emit) async { + Future _onBlankState( + BlankStateEvent event, Emitter emit) async { try { final previousState = state; final projectUuid = await ProjectManager.getProjectUUID() ?? ''; var spaceBloc = event.context.read(); var spaceTreeState = event.context.read().state; - List communities = await _waitForCommunityList(spaceBloc, spaceTreeState); + List communities = + await _waitForCommunityList(spaceBloc, spaceTreeState); await fetchSpaceModels(); await fetchTags(); var prevSpaceModels = await fetchSpaceModels(); - if (previousState is SpaceManagementLoaded || previousState is BlankState) { + if (previousState is SpaceManagementLoaded || + previousState is BlankState) { final prevCommunities = (previousState as dynamic).communities ?? []; emit(BlankState( communities: List.from(prevCommunities), @@ -286,7 +297,8 @@ class SpaceManagementBloc extends Bloc communities = await _waitForCommunityList(spaceBloc, spaceTreeState); + List communities = + await _waitForCommunityList(spaceBloc, spaceTreeState); // Fetch space models after communities are available final prevSpaceModels = await fetchSpaceModels(); @@ -310,8 +322,9 @@ class SpaceManagementBloc extends Bloc>(); final subscription = spaceBloc.stream.listen((state) { if (!completer.isCompleted && state.communityList.isNotEmpty) { - completer - .complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList); + completer.complete(state.searchQuery.isNotEmpty + ? state.filteredCommunity + : state.communityList); } }); try { @@ -339,7 +352,8 @@ class SpaceManagementBloc extends Bloc.from( (previousState as dynamic).communities, ); @@ -459,8 +474,8 @@ class SpaceManagementBloc extends Bloc().state; - final updatedSpaces = - await saveSpacesHierarchically(event.context, event.spaces, event.communityUuid); + final updatedSpaces = await saveSpacesHierarchically( + event.context, event.spaces, event.communityUuid); final allSpaces = await _fetchSpacesForCommunity(event.communityUuid); emit(SpaceCreationSuccess(spaces: updatedSpaces)); @@ -520,8 +535,8 @@ class SpaceManagementBloc extends Bloc> saveSpacesHierarchically( - BuildContext context, List spaces, String communityUuid) async { + Future> saveSpacesHierarchically(BuildContext context, + List spaces, String communityUuid) async { final orderedSpaces = flattenHierarchy(spaces); final projectUuid = await ProjectManager.getProjectUUID() ?? ''; @@ -575,17 +590,19 @@ class SpaceManagementBloc extends Bloc subspace.uuid == prevSubspace.uuid); + final existsInNew = newSubspaces + .any((subspace) => subspace.uuid == prevSubspace.uuid); if (!existsInNew) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: custom_action.Action.delete, uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, + uuid: prevSubspace.uuid)); } } } else if (prevSubspaces != null && newSubspaces == null) { for (var prevSubspace in prevSubspaces) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: custom_action.Action.delete, uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, + uuid: prevSubspace.uuid)); } } @@ -613,7 +630,9 @@ class SpaceManagementBloc extends Bloc tag.toCreateTagBodyModel()).toList() ?? []; + final tagBodyModels = subspace.tags + ?.map((tag) => tag.toCreateTagBodyModel()) + .toList() ?? + []; return CreateSubspaceModel() ..subspaceName = subspace.subspaceName ..tags = tagBodyModels; @@ -671,7 +691,6 @@ class SpaceManagementBloc extends Bloc emit) async { + void _onLoadSpaceModel( + SpaceModelLoadEvent event, Emitter emit) async { emit(SpaceManagementLoading()); try { @@ -757,14 +777,17 @@ class SpaceManagementBloc extends Bloc newTag.uuid == prevTag.uuid); + final existsInNew = + newTags.any((newTag) => newTag.uuid == prevTag.uuid); if (!existsInNew) { - tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate( + action: custom_action.Action.delete, uuid: prevTag.uuid)); } } } else if (prevTags != null && newTags == null) { for (var prevTag in prevTags) { - tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate( + action: custom_action.Action.delete, uuid: prevTag.uuid)); } } @@ -807,15 +830,16 @@ class SpaceManagementBloc extends Bloc findMatchingSpaces(List spaces, String targetUuid) { + List findMatchingSpaces( + List spaces, String targetUuid) { List matched = []; for (var space in spaces) { if (space.uuid == targetUuid) { matched.add(space); } - matched - .addAll(findMatchingSpaces(space.children, targetUuid)); // Recursively search in children + matched.addAll(findMatchingSpaces( + space.children, targetUuid)); // Recursively search in children } return matched; diff --git a/lib/pages/spaces_management/all_spaces/model/connection_model.dart b/lib/pages/spaces_management/all_spaces/model/connection_model.dart index a774efe2..0799d81e 100644 --- a/lib/pages/spaces_management/all_spaces/model/connection_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/connection_model.dart @@ -3,23 +3,26 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model class Connection { final SpaceModel startSpace; final SpaceModel endSpace; - final String direction; - Connection({required this.startSpace, required this.endSpace, required this.direction}); + Connection({ + required this.startSpace, + required this.endSpace, + }); 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, + 'startUuid': startSpace.uuid ?? + 'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces + 'endUuid': endSpace.uuid ?? + 'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces }; } - static Connection fromMap(Map map, Map spaces) { + 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/all_spaces/model/space_model.dart b/lib/pages/spaces_management/all_spaces/model/space_model.dart index 6e744a29..6b570004 100644 --- a/lib/pages/spaces_management/all_spaces/model/space_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/space_model.dart @@ -116,7 +116,7 @@ class SpaceModel { instance.incomingConnection = Connection( startSpace: instance.parent ?? instance, // Parent space endSpace: instance, // This space instance - direction: conn['direction'], + ); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart index 16ecae36..178f7659 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart @@ -199,13 +199,11 @@ class _CommunityStructureAreaState extends State { top: entry.value.position.dy, child: SpaceCardWidget( index: entry.key, - onButtonTap: (int index, Offset newPosition, - String direction) { + onButtonTap: (int index, Offset newPosition) { _showCreateSpaceDialog(screenSize, position: spaces[index].position + newPosition, parentIndex: index, - direction: direction, projectTags: widget.projectTags); }, position: entry.value.position, @@ -296,7 +294,6 @@ class _CommunityStructureAreaState extends State { void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, - String? direction, double? canvasWidth, double? canvasHeight, required List projectTags}) { @@ -338,14 +335,13 @@ class _CommunityStructureAreaState extends State { subspaces: subspaces, tags: tags); - if (parentIndex != null && direction != null) { + if (parentIndex != null) { SpaceModel parentSpace = spaces[parentIndex]; parentSpace.internalId = spaces[parentIndex].internalId; newSpace.parent = parentSpace; final newConnection = Connection( startSpace: parentSpace, endSpace: newSpace, - direction: direction, ); connections.add(newConnection); newSpace.incomingConnection = newConnection; @@ -467,7 +463,6 @@ class _CommunityStructureAreaState extends State { Connection( startSpace: parent, endSpace: child, - direction: "down", ), ); @@ -750,7 +745,6 @@ class _CommunityStructureAreaState extends State { final newConnection = Connection( startSpace: parent, endSpace: duplicated, - direction: "down", ); connections.add(newConnection); duplicated.incomingConnection = newConnection; @@ -786,7 +780,6 @@ class _CommunityStructureAreaState extends State { final newConnection = Connection( startSpace: newSpace, endSpace: duplicatedChild, - direction: "down", ); connections.add(newConnection); diff --git a/lib/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart b/lib/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart index 2b85acfd..d8291110 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart @@ -30,28 +30,13 @@ class CurvedLinePainter extends CustomPainter { Offset end = connection.endSpace.position + const 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') { - start = connection.startSpace.position + - const Offset(150, 30); // Right center - end = connection.endSpace.position + const Offset(0, 30); // Left center + // 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); - canvas.drawLine(start, end, paint); - } else if (connection.direction == 'left') { - 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); - } - 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/all_spaces/widgets/plus_button_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart index 280816a0..6c5babaf 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/plus_button_widget.dart @@ -5,7 +5,7 @@ class PlusButtonWidget extends StatelessWidget { final int index; final String direction; final Offset offset; - final Function(int index, Offset newPosition, String direction) onButtonTap; + final Function(int index, Offset newPosition) onButtonTap; const PlusButtonWidget({ super.key, @@ -19,21 +19,7 @@ class PlusButtonWidget extends StatelessWidget { Widget build(BuildContext context) { return GestureDetector( onTap: () { - Offset newPosition; - switch (direction) { - case 'left': - newPosition = const Offset(-200, 0); - break; - case 'right': - newPosition = const Offset(200, 0); - break; - case 'down': - newPosition = const Offset(0, 150); - break; - default: - newPosition = Offset.zero; - } - onButtonTap(index, newPosition, direction); + onButtonTap(index, const Offset(0, 150)); }, child: Container( width: 30, @@ -42,8 +28,11 @@ 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: ColorsManager.whiteColors, + size: 20, + ), ), ); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart index 49df9daa..7e6e132f 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/space_card_widget.dart @@ -7,7 +7,7 @@ class SpaceCardWidget extends StatelessWidget { final Offset position; final bool isHovered; final Function(int index, bool isHovered) onHoverChanged; - final Function(int index, Offset newPosition, String direction) onButtonTap; + final Function(int index, Offset newPosition) onButtonTap; final Widget Function(int index) buildSpaceContainer; final ValueChanged onPositionChanged; diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 19e219b6..a1372618 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -199,7 +199,7 @@ class CommunitySpaceManagementApi { {required String communityId, required String name, String? parentId, - String? direction, + bool isPrivate = false, required Offset position, String? spaceModelUuid, @@ -213,7 +213,7 @@ class CommunitySpaceManagementApi { 'isPrivate': isPrivate, 'x': position.dx, 'y': position.dy, - 'direction': direction, + 'icon': icon, }; if (parentId != null) { @@ -248,7 +248,7 @@ class CommunitySpaceManagementApi { required String name, String? parentId, String? icon, - String? direction, + bool isPrivate = false, required Offset position, List? tags, @@ -261,7 +261,7 @@ class CommunitySpaceManagementApi { 'isPrivate': isPrivate, 'x': position.dx, 'y': position.dy, - 'direction': direction, + 'icon': icon, 'subspace': subspaces, 'tags': tags, From a1d7457065fa6a45287505b747868cf9db717590 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Thu, 29 May 2025 14:34:15 +0300 Subject: [PATCH 22/41] Test commit with updated user --- .../spaces_management/create_subspace/bloc/subspace_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart index b334a301..a2d44553 100644 --- a/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart @@ -77,7 +77,7 @@ class SubSpaceBloc extends Bloc { emit(SubSpaceState( updatedSubSpaces, updatedSubspaceModels, - errorMessage, + errorMessage , updatedDuplicates, )); }); From 15d3a05553344a0c70e1407cc7ad797a79ad7663 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:03:32 +0300 Subject: [PATCH 23/41] assign tag dropDown now show all Tags without condition --- lib/common/tag_dialog_textfield_dropdown.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/tag_dialog_textfield_dropdown.dart b/lib/common/tag_dialog_textfield_dropdown.dart index 6bc22fc0..9fa85284 100644 --- a/lib/common/tag_dialog_textfield_dropdown.dart +++ b/lib/common/tag_dialog_textfield_dropdown.dart @@ -50,9 +50,9 @@ class _DialogTextfieldDropdownState extends State { void _filterItems() { setState(() { - _filteredItems = widget.items - .where((tag) => tag.product?.uuid == widget.product) - .toList(); + _filteredItems = widget.items; + // .where((tag) => tag.product?.uuid == widget.product) + // .toList(); }); } From 8e9278c93c32b5fc64e6b9578cc0a8d6ade8ba10 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:05:50 +0300 Subject: [PATCH 24/41] edits with faris and main task to fix loading state forever and prevent rethrow exceptions without catching them --- .../bloc/space_management_bloc.dart | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart index 04087257..e5c9432f 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart @@ -478,8 +478,11 @@ class SpaceManagementBloc event.context, event.spaces, event.communityUuid); final allSpaces = await _fetchSpacesForCommunity(event.communityUuid); - emit(SpaceCreationSuccess(spaces: updatedSpaces)); - + // emit(SpaceCreationSuccess(spaces: updatedSpaces)); + // updatedSpaces.forEach( + // (element) => element.uuid, + // ); + // final lastUpdatedSpaced = updatedSpaces..addAll(allSpaces); if (previousState is SpaceManagementLoaded) { await _updateLoadedState( spaceTreeState, @@ -490,7 +493,7 @@ class SpaceManagementBloc ); } } catch (e) { - emit(SpaceManagementError('Error saving spaces: $e')); + // emit(SpaceManagementError('Error saving spaces: $e')); if (previousState is SpaceManagementLoaded) { emit(previousState); @@ -530,8 +533,10 @@ class SpaceManagementBloc return; } } + emit(previousState); } catch (e, stackTrace) { - rethrow; + emit(previousState); + // rethrow; } } @@ -549,6 +554,14 @@ class SpaceManagementBloc selectedCommunity = filteredCommunities.firstWhere( (community) => community.uuid == communityUuid, + orElse: () => CommunityModel( + uuid: '', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + name: '', + description: '', + spaces: spaces, + ), ); } catch (e) { return []; @@ -563,9 +576,7 @@ class SpaceManagementBloc if (parent.uuid != null) { await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid); } - } catch (e) { - rethrow; - } + } catch (e) {} } orderedSpaces.removeWhere((space) => parentsToDelete.contains(space)); @@ -579,7 +590,7 @@ class SpaceManagementBloc if (matchedSpaces.isEmpty) continue; - final prevSpace = matchedSpaces[0]; + final prevSpace = matchedSpaces.elementAtOrNull(0); final List subspaceUpdates = []; final List? prevSubspaces = prevSpace?.subspaces; @@ -658,8 +669,10 @@ class SpaceManagementBloc isPrivate: space.isPrivate, position: space.position, icon: space.icon, - subspaces: subspaceUpdates, - tags: tagUpdates, + subspaces: space.subspaces, + // subspaceUpdates, + tags: space.tags, + // tagUpdates, spaceModelUuid: space.spaceModel?.uuid, projectId: projectUuid); } else { @@ -698,7 +711,8 @@ class SpaceManagementBloc space.uuid = response?.uuid; } } catch (e) { - rethrow; // Stop further execution on failure + return []; + // Stop further execution on failure } } return spaces; From 8f7bfa984b1ee87f6053a97ab6ccb1efbc2468a3 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:06:36 +0300 Subject: [PATCH 25/41] edit spacemodel to use the right keys to integrate with backend --- lib/pages/spaces_management/all_spaces/model/space_model.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/model/space_model.dart b/lib/pages/spaces_management/all_spaces/model/space_model.dart index 6b570004..4956d472 100644 --- a/lib/pages/spaces_management/all_spaces/model/space_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/space_model.dart @@ -101,7 +101,7 @@ class SpaceModel { spaceModel: json['spaceModel'] != null ? SpaceTemplateModel.fromJson(json['spaceModel']) : null, - tags: (json['tags'] as List?) + tags: (json['productAllocations'] as List?) ?.where((item) => item is Map) // Validate type .map((item) => Tag.fromJson(item as Map)) .toList() ?? @@ -116,7 +116,6 @@ class SpaceModel { instance.incomingConnection = Connection( startSpace: instance.parent ?? instance, // Parent space endSpace: instance, // This space instance - ); } From ca02de20934d6071a4c8d446b91d5994ab017b52 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:07:26 +0300 Subject: [PATCH 26/41] edit subspace model to fix keys and integrate with backend --- .../spaces_management/all_spaces/model/subspace_model.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/model/subspace_model.dart b/lib/pages/spaces_management/all_spaces/model/subspace_model.dart index a89ec409..fd3e780e 100644 --- a/lib/pages/spaces_management/all_spaces/model/subspace_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/subspace_model.dart @@ -27,7 +27,7 @@ class SubspaceModel { subspaceName: json['subspaceName'] ?? '', disabled: json['disabled'] ?? false, internalId: internalId, - tags: (json['tags'] as List?) + tags: (json['productAllocations'] as List?) ?.map((item) => Tag.fromJson(item)) .toList() ?? [], @@ -36,7 +36,7 @@ class SubspaceModel { Map toJson() { return { - 'uuid': uuid, + if (uuid != null) 'uuid': uuid, 'subspaceName': subspaceName, 'disabled': disabled, 'tags': tags?.map((e) => e.toJson()).toList() ?? [], From 1d30c753f54bdd580cc24215001b73948c643ef1 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:08:49 +0300 Subject: [PATCH 27/41] edit tag model keys to integrate with backend --- .../spaces_management/all_spaces/model/tag.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/model/tag.dart b/lib/pages/spaces_management/all_spaces/model/tag.dart index 8959986c..a7ec1e15 100644 --- a/lib/pages/spaces_management/all_spaces/model/tag.dart +++ b/lib/pages/spaces_management/all_spaces/model/tag.dart @@ -23,10 +23,13 @@ class Tag extends BaseTag { final String internalId = json['internalId'] ?? const Uuid().v4(); return Tag( - uuid: json['uuid'] ?? '', + //TODO:insure UUId for tag or prodAlloc + uuid: json['name'] != null ? json['uuid'] : json['tag']?['uuid'] ?? '', internalId: internalId, - tag: json['name'] ?? '', - product: json['product'] != null ? ProductModel.fromMap(json['product']) : null, + tag: json['name'] ?? json['tag']?['name'] ?? '', + product: json['product'] != null + ? ProductModel.fromMap(json['product']) + : null, ); } @@ -49,9 +52,10 @@ class Tag extends BaseTag { Map toJson() { return { - 'uuid': uuid, - 'tag': tag, - 'product': product?.toMap(), + if (uuid != null) 'uuid': uuid, + 'name': tag, + 'productUuid': product?.uuid, + // .toMap(), }; } } From d2a2d391e0af1e6edc24e7b8ea357130962611cb Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:19:48 +0300 Subject: [PATCH 28/41] send subspaces with onSave in okCancel inside subspace dialog as parameters --- .../views/create_subspace_model_dialog.dart | 7 ++----- .../views/widgets/ok_cancel_sub_space_widget.dart | 7 +++++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart index 501ad5a1..82df866a 100644 --- a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart @@ -1,14 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; -import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart'; -import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -20,7 +16,8 @@ class CreateSubSpaceDialog extends StatefulWidget { final List? existingSubSpaces; final String? spaceName; final List? products; - final void Function(List?)? onSave; + final void Function( + List?, List updatedSubSpaces)? onSave; const CreateSubSpaceDialog({ required this.dialogTitle, diff --git a/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart b/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart index 3c3db8f0..3952e105 100644 --- a/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart +++ b/lib/pages/spaces_management/create_subspace/views/widgets/ok_cancel_sub_space_widget.dart @@ -17,7 +17,8 @@ class OkCancelSubSpaceWidget extends StatelessWidget { }); final TextEditingController subspaceNameController; - final void Function(List?)? onSave; + final void Function( + List?, List updatedSubSpaces)? onSave; final String errorMessage; @override Widget build(BuildContext context) { @@ -53,7 +54,9 @@ class OkCancelSubSpaceWidget extends StatelessWidget { await Future.delayed(const Duration(milliseconds: 10)); final subSpaces = subSpacesBloc.state.subSpaces; - onSave?.call(subSpaces); + + onSave?.call( + subSpaces, subSpacesBloc.state.updatedSubSpaceModels); Navigator.of(context).pop(); } From e22bab00d9bc7b790c50a7bbeffbdf1231ce38f9 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 10:27:08 +0300 Subject: [PATCH 29/41] just format code in assign tag bloc --- .../assign_tag/bloc/assign_tag_bloc.dart | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart index 74161b6f..afc0c852 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart @@ -13,7 +13,8 @@ class AssignTagBloc extends Bloc { final existingTagCounts = {}; for (var tag in initialTags) { if (tag.product != null) { - existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1; + existingTagCounts[tag.product!.uuid] = + (existingTagCounts[tag.product!.uuid] ?? 0) + 1; } } @@ -22,14 +23,17 @@ class AssignTagBloc extends Bloc { for (var selectedProduct in event.addedProducts) { final existingCount = existingTagCounts[selectedProduct.productId] ?? 0; - if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) { - tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); + if (selectedProduct.count == 0 || + selectedProduct.count <= existingCount) { + tags.addAll(initialTags + .where((tag) => tag.product?.uuid == selectedProduct.productId)); continue; } final missingCount = selectedProduct.count - existingCount; - tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); + tags.addAll(initialTags + .where((tag) => tag.product?.uuid == selectedProduct.productId)); if (missingCount > 0) { tags.addAll(List.generate( @@ -85,7 +89,8 @@ class AssignTagBloc extends Bloc { final tags = List.from(currentState.tags); // Update the location - tags[event.index] = tags[event.index].copyWith(location: event.location); + tags[event.index] = + tags[event.index].copyWith(location: event.location); final updatedTags = _calculateAvailableTags(projectTags, tags); @@ -117,7 +122,8 @@ class AssignTagBloc extends Bloc { final currentState = state; if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags)..remove(event.tagToDelete); + final tags = List.from(currentState.tags) + ..remove(event.tagToDelete); // Recalculate available tags final updatedTags = _calculateAvailableTags(projectTags, tags); @@ -141,8 +147,10 @@ class AssignTagBloc extends Bloc { // Get validation error for duplicate tags String? _getValidationError(List tags) { - final nonEmptyTags = - tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList(); + final nonEmptyTags = tags + .map((tag) => tag.tag?.trim() ?? '') + .where((tag) => tag.isNotEmpty) + .toList(); final duplicateTags = nonEmptyTags .fold>({}, (map, tag) { @@ -168,9 +176,11 @@ class AssignTagBloc extends Bloc { .toSet(); final availableTags = allTags - .where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim())) + .where((tag) => + tag.tag != null && !selectedTagSet.contains(tag.tag!.trim())) .toList(); - return availableTags; + return projectTags; + // availableTags; } } From 0d5734a2365f67a358fa65ea78e2f5099c3117cc Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 15:06:31 +0300 Subject: [PATCH 30/41] use tag instead of UpdatedTagModel cuz no need for updatedmodel anymore&&key should be spaces with S --- lib/services/space_mana_api.dart | 68 +++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index a1372618..a20163da 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -4,22 +4,27 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_m import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; +import '../pages/spaces_management/all_spaces/model/subspace_model.dart'; + class CommunitySpaceManagementApi { // Community Management APIs - Future> fetchCommunities(String projectId, {int page = 1}) async { + Future> fetchCommunities(String projectId, + {int page = 1}) async { try { List allCommunities = []; bool hasNext = true; while (hasNext) { await HTTPService().get( - path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId), + path: ApiEndpoints.getCommunityList + .replaceAll('{projectId}', projectId), queryParameters: { 'page': page, }, @@ -55,8 +60,14 @@ class CommunitySpaceManagementApi { try { bool hasNext = false; await HTTPService().get( - path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId), - queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search}, + path: + ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId), + queryParameters: { + 'page': page, + 'includeSpaces': true, + 'size': 25, + 'search': search + }, expectedResponseModel: (json) { try { List jsonData = json['data'] ?? []; @@ -68,7 +79,10 @@ class CommunitySpaceManagementApi { page = currentPage + 1; paginationModel = PaginationModel( - pageNum: page, hasNext: hasNext, size: 25, communities: communityList); + pageNum: page, + hasNext: hasNext, + size: 25, + communities: communityList); return paginationModel; } catch (_) { hasNext = false; @@ -83,7 +97,8 @@ 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']); }, @@ -95,7 +110,8 @@ class CommunitySpaceManagementApi { } } - Future createCommunity(String name, String description, String projectId) async { + Future createCommunity( + String name, String description, String projectId) async { try { final response = await HTTPService().post( path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId), @@ -114,7 +130,8 @@ class CommunitySpaceManagementApi { } } - Future updateCommunity(String communityId, String name, String projectId) async { + Future updateCommunity( + String communityId, String name, String projectId) async { try { final response = await HTTPService().put( path: ApiEndpoints.updateCommunity @@ -151,7 +168,8 @@ class CommunitySpaceManagementApi { } } - Future fetchSpaces(String communityId, String projectId) async { + Future fetchSpaces( + String communityId, String projectId) async { try { final response = await HTTPService().get( path: ApiEndpoints.listSpaces @@ -177,7 +195,8 @@ class CommunitySpaceManagementApi { } } - Future getSpace(String communityId, String spaceId, String projectId) async { + Future getSpace( + String communityId, String spaceId, String projectId) async { try { final response = await HTTPService().get( path: ApiEndpoints.getSpace @@ -199,7 +218,6 @@ class CommunitySpaceManagementApi { {required String communityId, required String name, String? parentId, - bool isPrivate = false, required Offset position, String? spaceModelUuid, @@ -213,7 +231,6 @@ class CommunitySpaceManagementApi { 'isPrivate': isPrivate, 'x': position.dx, 'y': position.dy, - 'icon': icon, }; if (parentId != null) { @@ -248,11 +265,10 @@ class CommunitySpaceManagementApi { required String name, String? parentId, String? icon, - bool isPrivate = false, required Offset position, - List? tags, - List? subspaces, + List? tags, + List? subspaces, String? spaceModelUuid, required String projectId}) async { try { @@ -261,9 +277,8 @@ class CommunitySpaceManagementApi { 'isPrivate': isPrivate, 'x': position.dx, 'y': position.dy, - 'icon': icon, - 'subspace': subspaces, + 'subspaces': subspaces, 'tags': tags, 'spaceModelUuid': spaceModelUuid, }; @@ -289,7 +304,8 @@ class CommunitySpaceManagementApi { } } - Future deleteSpace(String communityId, String spaceId, String projectId) async { + Future deleteSpace( + String communityId, String spaceId, String projectId) async { try { final response = await HTTPService().delete( path: ApiEndpoints.deleteSpace @@ -307,15 +323,17 @@ class CommunitySpaceManagementApi { } } - Future> getSpaceHierarchy(String communityId, String projectId) async { + Future> getSpaceHierarchy( + String communityId, String projectId) async { try { final response = await HTTPService().get( path: ApiEndpoints.getSpaceHierarchy .replaceAll('{communityId}', communityId) .replaceAll('{projectId}', projectId), expectedResponseModel: (json) { - final spaceModels = - (json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList(); + final spaceModels = (json['data'] as List) + .map((spaceJson) => SpaceModel.fromJson(spaceJson)) + .toList(); return spaceModels; }, @@ -327,15 +345,17 @@ class CommunitySpaceManagementApi { } } - Future> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async { + Future> getSpaceOnlyWithDevices( + {String? communityId, String? projectId}) async { try { final response = await HTTPService().get( path: ApiEndpoints.spaceOnlyWithDevices .replaceAll('{communityId}', communityId!) .replaceAll('{projectId}', projectId!), expectedResponseModel: (json) { - final spaceModels = - (json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList(); + final spaceModels = (json['data'] as List) + .map((spaceJson) => SpaceModel.fromJson(spaceJson)) + .toList(); return spaceModels; }, ); From 7eb1d5b0b095a01ff7486ca0f490f7647904bb20 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 15:07:32 +0300 Subject: [PATCH 31/41] comment listSpace func which calls SpaceModels that suspended for now --- lib/services/space_model_mang_api.dart | 37 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index 5ae3e4d9..cbb9cfeb 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; @@ -7,17 +9,23 @@ import 'package:syncrow_web/utils/constants/api_const.dart'; class SpaceModelManagementApi { Future> listSpaceModels( {required String projectId, int page = 1}) async { - final response = await HTTPService().get( - path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId), - queryParameters: {'page': page}, - expectedResponseModel: (json) { - List jsonData = json['data']; - return jsonData.map((jsonItem) { - return SpaceTemplateModel.fromJson(jsonItem); - }).toList(); - }, - ); - return response; + try { + // final response = await HTTPService().get( + // path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId), + // queryParameters: {'page': page}, + // expectedResponseModel: (json) { + // List jsonData = json['data']; + // return jsonData.map((jsonItem) { + // return SpaceTemplateModel.fromJson(jsonItem); + // }).toList(); + // }, + // ); + return []; + // response; + } catch (e) { + log(e.toString()); + return []; + } } Future createSpaceModel( @@ -33,8 +41,8 @@ class SpaceModelManagementApi { return response; } - Future updateSpaceModel( - CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid, String projectId) async { + Future updateSpaceModel(CreateSpaceTemplateBodyModel spaceModel, + String spaceModelUuid, String projectId) async { final response = await HTTPService().put( path: ApiEndpoints.updateSpaceModel .replaceAll('{projectId}', projectId) @@ -47,7 +55,8 @@ class SpaceModelManagementApi { return response; } - Future getSpaceModel(String spaceModelUuid, String projectId) async { + Future getSpaceModel( + String spaceModelUuid, String projectId) async { final response = await HTTPService().get( path: ApiEndpoints.getSpaceModel .replaceAll('{projectId}', projectId) From 08a9a5c71f6ca1529870cc7d8eadef69735dbb16 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 15:08:50 +0300 Subject: [PATCH 32/41] comment the tab of spaceModel to prevent routing there --- .../view/center_body_widget.dart | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart b/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart index 0f40ddbb..0f63ebb1 100644 --- a/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart +++ b/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart @@ -14,11 +14,11 @@ class CenterBodyWidget extends StatelessWidget { if (state is InitialState) { context.read().add(CommunityStructureSelectedEvent()); } - if (state is CommunityStructureState) { + if (state is CommunityStructureState) { context.read().add(BlankStateEvent(context)); } - if (state is SpaceModelState) { + if (state is SpaceModelState) { context.read().add(SpaceModelLoadEvent(context)); } @@ -31,15 +31,19 @@ class CenterBodyWidget extends StatelessWidget { children: [ GestureDetector( onTap: () { - context.read().add(CommunityStructureSelectedEvent()); + context + .read() + .add(CommunityStructureSelectedEvent()); }, child: Text( 'Community Structure', style: Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: state is CommunityStructureState || state is CommunitySelectedState + fontWeight: state is CommunityStructureState || + state is CommunitySelectedState ? FontWeight.bold : FontWeight.normal, - color: state is CommunityStructureState || state is CommunitySelectedState + color: state is CommunityStructureState || + state is CommunitySelectedState ? Theme.of(context).textTheme.bodyLarge!.color : Theme.of(context) .textTheme @@ -50,26 +54,26 @@ class CenterBodyWidget extends StatelessWidget { ), ), const SizedBox(width: 20), - GestureDetector( - onTap: () { - context.read().add(SpaceModelSelectedEvent()); - }, - child: Text( - 'Space Model', - style: Theme.of(context).textTheme.bodyLarge!.copyWith( - fontWeight: state is SpaceModelState - ? FontWeight.bold - : FontWeight.normal, - color: state is SpaceModelState - ? Theme.of(context).textTheme.bodyLarge!.color - : Theme.of(context) - .textTheme - .bodyLarge! - .color! - .withOpacity(0.5), - ), - ), - ), + // GestureDetector( + // onTap: () { + // context.read().add(SpaceModelSelectedEvent()); + // }, + // child: Text( + // 'Space Model', + // style: Theme.of(context).textTheme.bodyLarge!.copyWith( + // fontWeight: state is SpaceModelState + // ? FontWeight.bold + // : FontWeight.normal, + // color: state is SpaceModelState + // ? Theme.of(context).textTheme.bodyLarge!.color + // : Theme.of(context) + // .textTheme + // .bodyLarge! + // .color! + // .withOpacity(0.5), + // ), + // ), + // ), ], ), ], From 692c9e7792904ce4b121607cceae566f60f47973 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 15:11:33 +0300 Subject: [PATCH 33/41] comment (SpaceModelLinking && Or)Widgets cuz no need for spaceModel for now&& fix the delete devices from subSpaces to keep in main space not to get deleted --- .../widgets/dialogs/create_space_dialog.dart | 141 +++++++++++------- 1 file changed, 89 insertions(+), 52 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index eaadf671..9e2f6dbb 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -155,63 +155,76 @@ class CreateSpaceDialogState extends State { }, ), const SizedBox(height: 10), - SpaceModelLinkingWidget( - isSpaceModelDisabled: isSpaceModelDisabled, - onPressed: () { - isSpaceModelDisabled - ? null - : _showLinkSpaceModelDialog(context); - }, - onDeleted: () => setState(() { - selectedSpaceModel = null; - subspaces = widget.subspaces ?? []; - tags = widget.tags ?? []; - }), - screenWidth: screenWidth, - selectedSpaceModel: selectedSpaceModel, - ), + // SpaceModelLinkingWidget( + // isSpaceModelDisabled: true, + // // isSpaceModelDisabled, + // onPressed: () { + // isSpaceModelDisabled + // ? null + // : _showLinkSpaceModelDialog(context); + // }, + // onDeleted: () => setState(() { + // selectedSpaceModel = null; + // subspaces = widget.subspaces ?? []; + // tags = widget.tags ?? []; + // }), + // screenWidth: screenWidth, + // selectedSpaceModel: selectedSpaceModel, + // ), const SizedBox(height: 25), - Row( - children: [ - const Expanded( - child: Divider( - color: ColorsManager.neutralGray, - thickness: 1.0, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 6.0), - child: Text( - 'OR', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith(fontWeight: FontWeight.bold), - ), - ), - const Expanded( - child: Divider( - color: ColorsManager.neutralGray, - thickness: 1.0, - ), - ), - ], - ), + // Row( + // children: [ + // const Expanded( + // child: Divider( + // color: ColorsManager.neutralGray, + // thickness: 1.0, + // ), + // ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 6.0), + // child: Text( + // 'OR', + // style: Theme.of(context) + // .textTheme + // .bodyMedium + // ?.copyWith(fontWeight: FontWeight.bold), + // ), + // ), + // const Expanded( + // child: Divider( + // color: ColorsManager.neutralGray, + // thickness: 1.0, + // ), + // ), + // ], + // ), const SizedBox(height: 25), SubSpacePartWidget( subspaces: subspaces, onPressed: () { isTagsAndSubspaceModelDisabled ? null - : _showSubSpaceDialog(context, enteredName, [], - false, widget.products, subspaces); + : _showSubSpaceDialog( + context, + enteredName, + [], + false, + widget.products, + subspaces, + ); }, isTagsAndSubspaceModelDisabled: isTagsAndSubspaceModelDisabled, screenWidth: screenWidth, editChipOnTap: () async { - _showSubSpaceDialog(context, enteredName, [], true, - widget.products, subspaces); + _showSubSpaceDialog( + context, + enteredName, + [], + true, + widget.products, + subspaces, + ); }, ), const SizedBox(height: 10), @@ -289,6 +302,13 @@ class CreateSpaceDialogState extends State { ? enteredName : (widget.name ?? ''); if (newName.isNotEmpty) { + if (tags != null && tags!.isNotEmpty) { + if (tags!.any( + (tag) => tag.uuid == null || tag.uuid!.isEmpty, + )) { + return; + } + } widget.onCreateSpace( newName, selectedIcon, @@ -352,12 +372,13 @@ class CreateSpaceDialogState extends State { } void _showSubSpaceDialog( - BuildContext context, - String name, - final List? spaceTags, - bool isEdit, - List? products, - final List? existingSubSpaces) { + BuildContext context, + String name, + final List? spaceTags, + bool isEdit, + List? products, + final List? existingSubSpaces, + ) { showDialog( context: context, builder: (BuildContext context) { @@ -366,7 +387,7 @@ class CreateSpaceDialogState extends State { dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces', products: products, existingSubSpaces: existingSubSpaces, - onSave: (slectedSubspaces) { + onSave: (slectedSubspaces, updatedSubSpaces) { final List tagsToAppendToSpace = []; if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) { @@ -378,6 +399,22 @@ class CreateSpaceDialogState extends State { .toList(); for (var s in deletedSubspaces) { if (s.tags != null) { + s.tags!.forEach( + (tag) => tag.location = null, + ); + tagsToAppendToSpace.addAll(s.tags!); + } + } + } + } else { + if (existingSubSpaces != null) { + final deletedSubspaces = existingSubSpaces; + + for (var s in deletedSubspaces) { + if (s.tags != null) { + s.tags!.forEach( + (tag) => tag.location = null, + ); tagsToAppendToSpace.addAll(s.tags!); } } From fd192894cdb36fae7d5629c4ff59e7fb43e03932 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 16:32:33 +0300 Subject: [PATCH 34/41] stop calling initEvent for spactree bloc inside homeBloc --- lib/pages/home/bloc/home_bloc.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/pages/home/bloc/home_bloc.dart b/lib/pages/home/bloc/home_bloc.dart index 33d55628..bc6a3165 100644 --- a/lib/pages/home/bloc/home_bloc.dart +++ b/lib/pages/home/bloc/home_bloc.dart @@ -51,12 +51,13 @@ class HomeBloc extends Bloc { Future _fetchUserInfo(FetchUserInfo event, Emitter emit) 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); if (user != null && user!.project != null) { await ProjectManager.setProjectUUID(user!.project!.uuid); - NavigationService.navigatorKey.currentContext!.read().add(InitialEvent()); + // NavigationService.navigatorKey.currentContext!.read().add(InitialEvent()); } add(FetchTermEvent()); add(FetchPolicyEvent()); @@ -88,10 +89,12 @@ class HomeBloc extends Bloc { } } - Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter emit) async { + Future _confirmUserAgreement( + ConfirmUserAgreementEvent event, Emitter emit) async { try { emit(LoadingHome()); - var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey); + var uuid = + await const FlutterSecureStorage().read(key: UserModel.userUuidKey); policy = await HomeApi().confirmUserAgreements(uuid); emit(PolicyAgreement()); } catch (e) { @@ -155,7 +158,7 @@ class HomeBloc extends Bloc { }, color: ColorsManager.primaryColor, ), - + // HomeItemModel( // title: 'Move in', // icon: Assets.moveinIcon, From 0e31a3ea96bd78e2f295083d638cdf5937f5ed94 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 16:33:33 +0300 Subject: [PATCH 35/41] no need to fetch use info in init state of homepage and agreement dialog --- lib/pages/home/view/home_page_web.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/pages/home/view/home_page_web.dart b/lib/pages/home/view/home_page_web.dart index 9a59f51c..a7a0eee4 100644 --- a/lib/pages/home/view/home_page_web.dart +++ b/lib/pages/home/view/home_page_web.dart @@ -24,7 +24,7 @@ class _HomeWebPageState extends State { void initState() { super.initState(); final homeBloc = BlocProvider.of(context); - homeBloc.add(const FetchUserInfo()); + // homeBloc.add(const FetchUserInfo()); } @override @@ -38,8 +38,10 @@ class _HomeWebPageState extends State { child: BlocConsumer( listener: (BuildContext context, state) { if (state is HomeInitial) { - if (homeBloc.user!.hasAcceptedWebAgreement == false && !_dialogShown) { - _dialogShown = true; // Set the flag to true to indicate the dialog is showing. + if (homeBloc.user!.hasAcceptedWebAgreement == false && + !_dialogShown) { + _dialogShown = + true; // Set the flag to true to indicate the dialog is showing. Future.delayed(const Duration(seconds: 1), () { showDialog( context: context, @@ -54,7 +56,7 @@ class _HomeWebPageState extends State { _dialogShown = false; if (v != null) { homeBloc.add(ConfirmUserAgreementEvent()); - homeBloc.add(const FetchUserInfo()); + // homeBloc.add(const FetchUserInfo()); } }); }); @@ -98,7 +100,8 @@ class _HomeWebPageState extends State { width: size.width * 0.68, child: GridView.builder( itemCount: homeBloc.homeItems.length, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, // Adjust as needed. crossAxisSpacing: 20.0, mainAxisSpacing: 20.0, @@ -110,7 +113,8 @@ class _HomeWebPageState extends State { active: homeBloc.homeItems[index].active!, name: homeBloc.homeItems[index].title!, img: homeBloc.homeItems[index].icon!, - onTap: () => homeBloc.homeItems[index].onPress(context), + onTap: () => + homeBloc.homeItems[index].onPress(context), ); }, ), From aa3b79bdafbe5df540922397986a700dc7362a63 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Tue, 10 Jun 2025 16:34:49 +0300 Subject: [PATCH 36/41] stop fetching tags onBlank Event --- .../all_spaces/bloc/space_management_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart index e5c9432f..f666c078 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart @@ -261,7 +261,7 @@ class SpaceManagementBloc List communities = await _waitForCommunityList(spaceBloc, spaceTreeState); await fetchSpaceModels(); - await fetchTags(); + // await fetchTags(); var prevSpaceModels = await fetchSpaceModels(); From 5ddfb4797763febc026b50856dd225301f846f7d Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Wed, 11 Jun 2025 12:54:38 +0300 Subject: [PATCH 37/41] Delete unused File --- lib/core/network/custom_exceptions.dart | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 lib/core/network/custom_exceptions.dart diff --git a/lib/core/network/custom_exceptions.dart b/lib/core/network/custom_exceptions.dart deleted file mode 100644 index 23b81a8d..00000000 --- a/lib/core/network/custom_exceptions.dart +++ /dev/null @@ -1,16 +0,0 @@ - -import 'enums.dart'; - -class GenericException implements Exception { - final ExceptionType type; - final String errorMessage; - const GenericException( - {required this.type, this.errorMessage = "Unknown Error"}); - - @override - String toString() { - return errorMessage; - } - - List get props => [type, errorMessage]; -} From 940b17968652ffc78ceb3f5578fa0b5f2410bfd8 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Wed, 11 Jun 2025 12:55:14 +0300 Subject: [PATCH 38/41] Delete unused file --- lib/core/network/dio.dart | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 lib/core/network/dio.dart diff --git a/lib/core/network/dio.dart b/lib/core/network/dio.dart deleted file mode 100644 index e01fef90..00000000 --- a/lib/core/network/dio.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:dio/dio.dart'; - -import '../../utils/constants/api_const.dart'; - -class DioInstance { - Dio? _dio; - - Dio get dio => _dio ?? _instantiate(); - - String? baseUrl; - - DioInstance({this.baseUrl}); - - Dio _instantiate() { - Dio dio = Dio( - BaseOptions( - baseUrl: baseUrl ?? '${ApiEndpoints.baseUrl}/', - receiveDataWhenStatusError: true, - headers: { - 'content_Type': 'application/json', - }, - ), - ); - - dio.interceptors.add( - LogInterceptor( - responseHeader: false, - requestHeader: false, - requestBody: true, - responseBody: true, - ), - ); - - return dio; - } -} From 329a4ef0276c17a31a5b1d84645a9d396e1fedfc Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Wed, 11 Jun 2025 12:55:54 +0300 Subject: [PATCH 39/41] Delete --- lib/core/network/end_points.dart | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 lib/core/network/end_points.dart diff --git a/lib/core/network/end_points.dart b/lib/core/network/end_points.dart deleted file mode 100644 index 3e2afd84..00000000 --- a/lib/core/network/end_points.dart +++ /dev/null @@ -1,3 +0,0 @@ -class EndPoints { - static const String fetchCommunities = 'projects/{projectUuid}/communities'; -} From 08f8c3c79a97e6a93458c86c7b614c1d5a355398 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Wed, 11 Jun 2025 12:56:28 +0300 Subject: [PATCH 40/41] Delete unused File --- lib/core/network/enums.dart | 50 ------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 lib/core/network/enums.dart diff --git a/lib/core/network/enums.dart b/lib/core/network/enums.dart deleted file mode 100644 index 488d7cb9..00000000 --- a/lib/core/network/enums.dart +++ /dev/null @@ -1,50 +0,0 @@ -enum RequestType { - get, - post, - delete, - put, -} - -enum ExceptionType { - notAuthenticated, - connectionError, - // related to http status code exceptions - notAuthorized, - notFound, - internalServerException, - serviceUnavailableException, - pageGone, - - // related to bad request status code - // related to auth requests - invalidCredentials, - solutionAlreadySunmitted, - invalidValidation, - // other - other, -} - -enum ExceptionMessage { - NOT_AUTHENTICATED, - INVALID_CREDENTIALS, - The_password_field_must_be_at_least_8_characters, - SOLUTION_ALREADY_SUBMITTED, - you_are_not_authorized, - page_not_found, - page_gone, - INTERNAL_SERVER_ERROR, - service_unavailable, -} - -enum NotificationsType { - payment, - transporation, - product, - zero, -} - -enum ImagesType { - assets, - svg, - network, -} From f415aa16762d9ebc7eaa0118ed5caab162e5c4e8 Mon Sep 17 00:00:00 2001 From: raf-dev1 Date: Wed, 11 Jun 2025 12:57:00 +0300 Subject: [PATCH 41/41] Delete unused File --- lib/core/network/request.dart | 121 ---------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 lib/core/network/request.dart diff --git a/lib/core/network/request.dart b/lib/core/network/request.dart deleted file mode 100644 index c242b8a6..00000000 --- a/lib/core/network/request.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - -import '../../pages/auth/model/token.dart'; -import 'custom_exceptions.dart'; -import 'dio.dart'; -import 'enums.dart'; - -class Request { - String endPoint; - bool? autherized; - bool? isFormData; - RequestType? method; - Map? headers; - final Map? queryParams; - Map? body; - Duration? receiveTimeout; - Request( - this.endPoint, { - this.autherized, - this.isFormData, - this.method, - this.headers, - this.queryParams, - this.body, - this.receiveTimeout, - }) { - headers = { - 'content_Type': 'application/json', - 'Accept': 'application/json', - }; - } - Future> sendRequest() async { - Response? response; - if (autherized != null && autherized!) { - final storage = const FlutterSecureStorage(); - final token = await storage.read(key: Token.loginAccessTokenKey); - if (token != null) { - headers!["authorization"] = "Bearer $token"; - } - } - try { - response = await DioInstance().dio.request( - endPoint, - queryParameters: queryParams, - data: isFormData != null && isFormData == true - ? FormData.fromMap(body!) - : body, - options: Options( - method: method!.name.toUpperCase(), - headers: headers, - contentType: 'application/json', - receiveTimeout: receiveTimeout, - ), - ); - if (response.statusCode! >= 200 && response.statusCode! < 300) { - return response.data; - } - } on DioException catch (error) { - if (error.type == DioExceptionType.badResponse) { - throw badRequestException[error.response!.data["error"]] ?? - const GenericException( - type: ExceptionType.other, - ); - } - if (error.type == DioExceptionType.connectionError || - error.type == DioExceptionType.connectionTimeout || - error.type == DioExceptionType.receiveTimeout || - error.type == DioExceptionType.sendTimeout || - error.type == DioExceptionType.unknown) { - throw const GenericException( - type: ExceptionType.connectionError, - errorMessage: 'no_internet_connection', - ); - } - } - return {}; - } - - Map toJson() => { - 'endPoint': endPoint, - 'method': method!.name.toUpperCase(), - 'body': json.encode(body), - 'headers': headers, - 'queryParams': queryParams, - 'autherized': autherized, - 'isFormData': isFormData, - }; - @override - String toString() { - return jsonEncode(toJson()); - } - - List get props => [ - endPoint, - autherized, - isFormData, - method, - headers, - queryParams, - body, - receiveTimeout, - ]; -} - -Map badRequestException = { - ExceptionMessage.INVALID_CREDENTIALS.name: const GenericException( - type: ExceptionType.invalidCredentials, - errorMessage: "Invalid credentials ...", - ), - "The password field must be at least 8 characters.": const GenericException( - type: ExceptionType.invalidValidation, - errorMessage: 'password must be 8 or more characters', - ), - ExceptionMessage.NOT_AUTHENTICATED.name: const GenericException( - type: ExceptionType.notAuthenticated, - errorMessage: "not authenticated", - ), -};