mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Compare commits
24 Commits
build_main
...
SP-1493-da
Author | SHA1 | Date | |
---|---|---|---|
5a8ef578c3 | |||
cedef666f6 | |||
a10d998ec6 | |||
ed50ac03d3 | |||
12deceb7d3 | |||
9d27ed2dc5 | |||
a878b9328a | |||
6606491458 | |||
92abcdc4f9 | |||
7aa9e7e5dc | |||
e9abac7933 | |||
0f9227a6f5 | |||
5b13962d41 | |||
8c53d5322a | |||
af4d37939b | |||
d43c1847ff | |||
4c5b390887 | |||
5eeac01666 | |||
717d698378 | |||
9adbbb9a2d | |||
e792dbd72f | |||
d2eea33714 | |||
5a61647fe4 | |||
568b6be354 |
19
assets/icons/landing_analytics.svg
Normal file
19
assets/icons/landing_analytics.svg
Normal file
@ -0,0 +1,19 @@
|
||||
<svg width="101" height="101" viewBox="0 0 101 101" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_9554_2115)">
|
||||
<path d="M93.8334 86.1048C97.5937 86.1048 100.653 83.0454 100.653 79.2849V9.83487C100.653 4.4501 96.2722 0.0692444 90.8875 0.0692444H10.4187C5.03394 0.0692444 0.653076 4.4501 0.653076 9.83487V79.2849C0.653076 83.0454 3.71245 86.1048 7.4728 86.1048H9.63745V96.163H2.6062C1.52749 96.163 0.653076 97.0376 0.653076 98.1161C0.653076 99.1946 1.52749 100.069 2.6062 100.069H98.7C99.7787 100.069 100.653 99.1946 100.653 98.1161C100.653 97.0376 99.7787 96.163 98.7 96.163H91.6687V86.1048H93.8334ZM4.55933 9.83487C4.55933 6.60401 7.18784 3.97549 10.4187 3.97549H90.8875C94.1183 3.97549 96.7468 6.60401 96.7468 9.83487V14.1317H4.75542C4.68921 14.1317 4.62378 14.1353 4.55933 14.1417V9.83487ZM22.1375 96.163H13.5437V78.5849H22.1375V96.163ZM44.0125 96.163H35.4187V56.9896H44.0125V96.163ZM65.8875 96.163H57.2937V73.5067H65.8875V96.163ZM89.7156 50.1669H77.2156C76.1369 50.1669 75.2625 51.0415 75.2625 52.12V61.9831C75.2625 63.0616 76.1369 63.9362 77.2156 63.9362C78.2943 63.9362 79.1687 63.0616 79.1687 61.9831V54.0732H87.7625V96.163H79.1687V75.1181C79.1687 74.0396 78.2943 73.1649 77.2156 73.1649C76.1369 73.1649 75.2625 74.0396 75.2625 75.1181V96.163H69.7937V71.5536C69.7937 70.4751 68.9193 69.6005 67.8406 69.6005H55.3406C54.2619 69.6005 53.3875 70.4751 53.3875 71.5536V96.163H47.9187V55.0364C47.9187 53.9579 47.0443 53.0833 45.9656 53.0833H33.4656C32.3869 53.0833 31.5125 53.9579 31.5125 55.0364V96.163H26.0437V76.6317C26.0437 75.5532 25.1693 74.6786 24.0906 74.6786H11.5906C10.5119 74.6786 9.63745 75.5532 9.63745 76.6317V82.1985H7.4728C5.86616 82.1985 4.55933 80.8915 4.55933 79.2849V18.028C4.62378 18.0345 4.68921 18.038 4.75542 18.038H96.7468V79.2849C96.7468 80.8915 95.4398 82.1985 93.8334 82.1985H91.6687V52.12C91.6687 51.0415 90.7943 50.1669 89.7156 50.1669Z" fill="white"/>
|
||||
<path d="M88.1531 7.10049H44.0251C42.9464 7.10049 42.072 7.9751 42.072 9.05362C42.072 10.1321 42.9464 11.0067 44.0251 11.0067H88.1531C89.2318 11.0067 90.1062 10.1321 90.1062 9.05362C90.1062 7.9751 89.2318 7.10049 88.1531 7.10049Z" fill="white"/>
|
||||
<path d="M22.3464 7.67276C21.9832 7.30948 21.4792 7.10049 20.9656 7.10049C20.4519 7.10049 19.948 7.30928 19.5847 7.67276C19.2214 8.03604 19.0125 8.53995 19.0125 9.05362C19.0125 9.56729 19.2214 10.0712 19.5847 10.4343C19.948 10.7978 20.4519 11.0067 20.9656 11.0067C21.4792 11.0067 21.9832 10.7978 22.3464 10.4343C22.7097 10.0712 22.9187 9.56729 22.9187 9.05362C22.9187 8.53995 22.7097 8.03604 22.3464 7.67276Z" fill="white"/>
|
||||
<path d="M14.5339 7.67276C14.1707 7.30948 13.6667 7.10049 13.1531 7.10049C12.6394 7.10049 12.1355 7.30928 11.7722 7.67276C11.4089 8.03604 11.2 8.53995 11.2 9.05362C11.2 9.56729 11.4089 10.0712 11.7722 10.4343C12.1355 10.7978 12.6394 11.0067 13.1531 11.0067C13.6667 11.0067 14.1707 10.7978 14.5339 10.4343C14.8972 10.0712 15.1062 9.56729 15.1062 9.05362C15.1062 8.53995 14.8972 8.03604 14.5339 7.67276Z" fill="white"/>
|
||||
<path d="M30.1589 7.67276C29.7957 7.30948 29.2937 7.10049 28.7781 7.10049C28.2644 7.10049 27.7605 7.30928 27.3972 7.67276C27.0339 8.03604 26.825 8.53995 26.825 9.05362C26.825 9.56729 27.0339 10.0712 27.3972 10.4343C27.7605 10.7978 28.2644 11.0067 28.7781 11.0067C29.2917 11.0067 29.7957 10.7978 30.1589 10.4343C30.5222 10.0712 30.7312 9.56729 30.7312 9.05362C30.7312 8.53995 30.5222 8.03604 30.1589 7.67276Z" fill="white"/>
|
||||
<path d="M78.5964 67.5634C78.2332 67.1981 77.7292 66.9911 77.2156 66.9911C76.7019 66.9911 76.198 67.1983 75.8347 67.5634C75.4714 67.9267 75.2625 68.4306 75.2625 68.9442C75.2625 69.4579 75.4714 69.9618 75.8347 70.3251C76.198 70.6882 76.7019 70.8974 77.2156 70.8974C77.7292 70.8974 78.2332 70.6884 78.5964 70.3251C78.9597 69.9618 79.1687 69.4579 79.1687 68.9442C79.1687 68.4286 78.9597 67.9267 78.5964 67.5634Z" fill="white"/>
|
||||
<path d="M83.4656 26.6198C79.5885 26.6198 76.4344 29.7739 76.4344 33.6511C76.4344 35.1911 76.9334 36.6161 77.7764 37.7757L64.8027 50.7493C63.7959 50.1921 62.6393 49.8737 61.4092 49.8737C60.3008 49.8737 59.252 50.1325 58.3186 50.5913L44.7719 37.0446C45.3291 36.0378 45.6475 34.8812 45.6475 33.6511C45.6475 29.7739 42.4934 26.6198 38.6162 26.6198C34.7391 26.6198 31.585 29.7739 31.585 33.6511C31.585 35.0062 31.9713 36.2722 32.6381 37.3468L21.1043 48.8804C20.0975 48.3232 18.9408 48.0048 17.7109 48.0048C13.8338 48.0048 10.6797 51.1589 10.6797 55.0361C10.6797 58.9132 13.8338 62.0673 17.7109 62.0673C21.5881 62.0673 24.7422 58.9132 24.7422 55.0361C24.7422 53.806 24.4238 52.6493 23.8666 51.6425L35.5381 39.971C36.4684 40.4259 37.5129 40.6821 38.6162 40.6821C39.8461 40.6821 41.0027 40.3638 42.0096 39.8066L55.4236 53.2204C54.7613 54.2927 54.3779 55.5544 54.3779 56.9046C54.3779 60.7818 57.532 63.9359 61.4092 63.9359C65.2863 63.9359 68.4404 60.7818 68.4404 56.9046C68.4404 55.6745 68.1221 54.5179 67.5648 53.5111L80.8861 40.1898C81.6855 40.5066 82.5553 40.6823 83.4656 40.6823C87.3428 40.6823 90.4969 37.5282 90.4969 33.6511C90.4969 29.7739 87.3428 26.6198 83.4656 26.6198ZM17.7111 58.1614C15.9881 58.1614 14.5861 56.7595 14.5861 55.0364C14.5861 53.3134 15.9881 51.9114 17.7111 51.9114C19.4342 51.9114 20.8361 53.3134 20.8361 55.0364C20.8361 56.7595 19.4342 58.1614 17.7111 58.1614ZM38.6164 36.7761C36.8934 36.7761 35.4914 35.3741 35.4914 33.6511C35.4914 31.928 36.8934 30.5261 38.6164 30.5261C40.3395 30.5261 41.7414 31.928 41.7414 33.6511C41.7414 35.3741 40.3395 36.7761 38.6164 36.7761ZM61.4094 60.03C59.6863 60.03 58.2844 58.628 58.2844 56.905C58.2844 55.182 59.6863 53.78 61.4094 53.78C63.1324 53.78 64.5344 55.182 64.5344 56.905C64.5344 58.628 63.1324 60.03 61.4094 60.03ZM83.4656 36.7761C81.7426 36.7761 80.3406 35.3741 80.3406 33.6511C80.3406 31.928 81.7426 30.5261 83.4656 30.5261C85.1887 30.5261 86.5906 31.928 86.5906 33.6511C86.5906 35.3741 85.1887 36.7761 83.4656 36.7761Z" fill="white"/>
|
||||
<path d="M29.6238 25.255C29.2585 24.8917 28.7566 24.6825 28.2429 24.6825C27.7273 24.6825 27.2234 24.8915 26.8601 25.255C26.4968 25.6181 26.2898 26.122 26.2898 26.6357C26.2898 27.1493 26.4968 27.6532 26.8601 28.0165C27.2253 28.3798 27.7273 28.5888 28.2429 28.5888C28.7566 28.5888 29.2585 28.38 29.6238 28.0165C29.9871 27.6532 30.196 27.1493 30.196 26.6357C30.196 26.122 29.9871 25.6181 29.6238 25.255Z" fill="white"/>
|
||||
<path d="M20.9656 24.6827H11.5906C10.5119 24.6827 9.63745 25.5573 9.63745 26.6358C9.63745 27.7143 10.5119 28.589 11.5906 28.589H20.9656C22.0443 28.589 22.9187 27.7143 22.9187 26.6358C22.9187 25.5573 22.0443 24.6827 20.9656 24.6827Z" fill="white"/>
|
||||
<path d="M20.9656 34.0577H11.5906C10.5119 34.0577 9.63745 34.9323 9.63745 36.0108C9.63745 37.0893 10.5119 37.964 11.5906 37.964H20.9656C22.0443 37.964 22.9187 37.0893 22.9187 36.0108C22.9187 34.9323 22.0443 34.0577 20.9656 34.0577Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_9554_2115">
|
||||
<rect width="100" height="100" fill="white" transform="translate(0.653076 0.0692444)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 6.8 KiB |
@ -17,8 +17,7 @@ class TagDialogTextfieldDropdown extends StatefulWidget {
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DialogTextfieldDropdownState createState() =>
|
||||
_DialogTextfieldDropdownState();
|
||||
_DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState();
|
||||
}
|
||||
|
||||
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||
@ -37,12 +36,6 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||
|
||||
_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();
|
||||
}
|
||||
});
|
||||
@ -50,9 +43,7 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
@ -121,9 +112,7 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor)),
|
||||
?.copyWith(color: ColorsManager.textPrimaryColor)),
|
||||
onTap: () {
|
||||
_controller.text = tag.tag ?? '';
|
||||
widget.onSelected(tag);
|
||||
@ -167,15 +156,13 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||
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();
|
||||
},
|
||||
|
@ -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<Object?> get props => [type, errorMessage];
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
class EndPoints {
|
||||
static const String fetchCommunities = 'projects/{projectUuid}/communities';
|
||||
}
|
@ -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,
|
||||
}
|
@ -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<String, dynamic>? headers;
|
||||
final Map<String, dynamic>? queryParams;
|
||||
Map<String, dynamic>? 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<Map<String, dynamic>> 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<String, dynamic> 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<Object?> get props => [
|
||||
endPoint,
|
||||
autherized,
|
||||
isFormData,
|
||||
method,
|
||||
headers,
|
||||
queryParams,
|
||||
body,
|
||||
receiveTimeout,
|
||||
];
|
||||
}
|
||||
|
||||
Map<String, GenericException> 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",
|
||||
),
|
||||
};
|
@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/params/get_analytics_devices_param.dart';
|
||||
|
||||
abstract final class FetchAirQualityDataHelper {
|
||||
const FetchAirQualityDataHelper._();
|
||||
|
||||
static void loadAirQualityData(
|
||||
BuildContext context, {
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
}) {
|
||||
loadAnalyticsDevices(
|
||||
context,
|
||||
communityUuid: communityUuid,
|
||||
spaceUuid: spaceUuid,
|
||||
);
|
||||
}
|
||||
|
||||
static void clearAllData(BuildContext context) {
|
||||
context.read<AnalyticsDevicesBloc>().add(
|
||||
const ClearAnalyticsDeviceEvent(),
|
||||
);
|
||||
context.read<RealtimeDeviceChangesBloc>().add(
|
||||
const RealtimeDeviceChangesClosed(),
|
||||
);
|
||||
}
|
||||
|
||||
static void loadAnalyticsDevices(
|
||||
BuildContext context, {
|
||||
required String communityUuid,
|
||||
required String spaceUuid,
|
||||
}) {
|
||||
context.read<AnalyticsDevicesBloc>().add(
|
||||
LoadAnalyticsDevicesEvent(
|
||||
param: GetAnalyticsDevicesParam(
|
||||
communityUuid: communityUuid,
|
||||
spaceUuid: spaceUuid,
|
||||
deviceTypes: ['AQI'],
|
||||
requestType: AnalyticsDeviceRequestType.energyManagement,
|
||||
),
|
||||
onSuccess: (device) {
|
||||
context.read<RealtimeDeviceChangesBloc>()
|
||||
..add(const RealtimeDeviceChangesClosed())
|
||||
..add(RealtimeDeviceChangesStarted(device.uuid));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart';
|
||||
|
||||
class AirQualityView extends StatelessWidget {
|
||||
const AirQualityView({super.key});
|
||||
|
||||
static const _padding = EdgeInsetsDirectional.all(32);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final isMediumOrLess = constraints.maxWidth <= 900;
|
||||
final height = MediaQuery.sizeOf(context).height;
|
||||
if (isMediumOrLess) {
|
||||
return SingleChildScrollView(
|
||||
padding: _padding,
|
||||
child: Column(
|
||||
spacing: 32,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: height * 1.2,
|
||||
child: const AirQualityEndSideWidget(),
|
||||
),
|
||||
SizedBox(height: height * 0.5, child: const Placeholder()),
|
||||
SizedBox(height: height * 0.5, child: const Placeholder()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: _padding,
|
||||
height: height,
|
||||
child: const Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
spacing: 32,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
spacing: 20,
|
||||
children: [
|
||||
Expanded(child: Placeholder()),
|
||||
Expanded(child: Placeholder()),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(child: AirQualityEndSideWidget()),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class AirQualityEndSideWidget extends StatelessWidget {
|
||||
const AirQualityEndSideWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: subSectionContainerDecoration.copyWith(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
padding: const EdgeInsetsDirectional.all(32),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
Text(
|
||||
'Device ID:',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
SelectableText(
|
||||
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ??
|
||||
'N/A',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
child: SelectableText(
|
||||
'AQI Sensor',
|
||||
style: context.textTheme.headlineSmall?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.vividBlue.withValues(alpha: 0.6),
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.scaleDown,
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: AnalyticsDeviceDropdown(
|
||||
onChanged: (value) {
|
||||
context.read<AnalyticsDevicesBloc>().add(
|
||||
SelectAnalyticsDeviceEvent(value),
|
||||
);
|
||||
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
||||
context,
|
||||
deviceUuid: value.uuid,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/views/air_quality_view.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/views/analytics_energy_management_view.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/occupancy/views/analytics_occupancy_view.dart';
|
||||
|
||||
@ -10,6 +11,10 @@ enum AnalyticsPageTab {
|
||||
occupancy(
|
||||
title: 'Occupancy',
|
||||
child: AnalyticsOccupancyView(),
|
||||
),
|
||||
airQuality(
|
||||
title: 'Air Quality',
|
||||
child: AirQualityView(),
|
||||
);
|
||||
|
||||
const AnalyticsPageTab({
|
||||
|
@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
|
||||
final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
@override
|
||||
void onCommunitySelected(
|
||||
BuildContext context,
|
||||
CommunityModel community,
|
||||
List<SpaceModel> spaces,
|
||||
) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@override
|
||||
void onSpaceSelected(
|
||||
BuildContext context,
|
||||
CommunityModel community,
|
||||
SpaceModel space,
|
||||
) {
|
||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||
|
||||
if (isSpaceSelected) {
|
||||
clearData(context);
|
||||
return;
|
||||
}
|
||||
|
||||
spaceTreeBloc
|
||||
..add(const SpaceTreeClearSelectionEvent())
|
||||
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||
|
||||
FetchAirQualityDataHelper.loadAirQualityData(
|
||||
context,
|
||||
communityUuid: community.uuid,
|
||||
spaceUuid: space.uuid ?? '',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onChildSpaceSelected(
|
||||
BuildContext context,
|
||||
CommunityModel community,
|
||||
SpaceModel child,
|
||||
) {
|
||||
return onSpaceSelected(context, community, child);
|
||||
}
|
||||
|
||||
@override
|
||||
void clearData(BuildContext context) {
|
||||
context.read<SpaceTreeBloc>().add(const SpaceTreeClearSelectionEvent());
|
||||
FetchAirQualityDataHelper.clearAllData(context);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/air_quality_data_loading_strategy.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/analytics_data_loading_strategy.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/energy_management_data_loading_strategy.dart';
|
||||
import 'package:syncrow_web/pages/analytics/modules/analytics/strategies/occupancy_data_loading_strategy.dart';
|
||||
@ -9,6 +10,7 @@ abstract final class AnalyticsDataLoadingStrategyFactory {
|
||||
return switch (tab) {
|
||||
AnalyticsPageTab.energyManagement => EnergyManagementDataLoadingStrategy(),
|
||||
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
||||
AnalyticsPageTab.airQuality => AirQualityDataLoadingStrategy(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -68,9 +68,7 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
||||
CommunityModel community,
|
||||
SpaceModel child,
|
||||
) {
|
||||
if (child.children.isNotEmpty) {
|
||||
return onSpaceSelected(context, community, child);
|
||||
}
|
||||
return onSpaceSelected(context, community, child);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -48,7 +48,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||
CommunityModel community,
|
||||
SpaceModel child,
|
||||
) {
|
||||
onSpaceSelected(context, community, child);
|
||||
return onSpaceSelected(context, community, child);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -38,7 +38,7 @@ abstract final class EnergyManagementChartsHelper {
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
maxIncluded: false,
|
||||
minIncluded: true,
|
||||
minIncluded: false,
|
||||
interval: leftTitlesInterval,
|
||||
reservedSize: 110,
|
||||
getTitlesWidget: (value, meta) => Padding(
|
||||
@ -50,7 +50,7 @@ abstract final class EnergyManagementChartsHelper {
|
||||
value.formatNumberToKwh,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 12,
|
||||
color: ColorsManager.greyColor,
|
||||
color: ColorsManager.lightGreyColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -170,7 +170,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
||||
child: Text(
|
||||
month,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.greyColor,
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
|
@ -19,10 +19,12 @@ class EnergyConsumptionByPhasesChartBox extends StatelessWidget {
|
||||
decoration: secondarySection,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 20,
|
||||
children: [
|
||||
AnalyticsErrorWidget(state.errorMessage),
|
||||
EnergyConsumptionByPhasesTitle(isLoading: state.status == EnergyConsumptionByPhasesStatus.loading,),
|
||||
EnergyConsumptionByPhasesTitle(
|
||||
isLoading: state.status == EnergyConsumptionByPhasesStatus.loading,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: EnergyConsumptionByPhasesChart(
|
||||
energyData: state.chartData,
|
||||
|
@ -69,13 +69,18 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
||||
PowerClampEnergyStatus(
|
||||
iconPath: Assets.powerActiveIcon,
|
||||
title: 'Active',
|
||||
value: _valueFromCode('EnergyConsumed', generalDataPoints),
|
||||
value: _valueFromCode('ActivePower', generalDataPoints),
|
||||
unit: 'W',
|
||||
),
|
||||
PowerClampEnergyStatus(
|
||||
iconPath: Assets.voltMeterIcon,
|
||||
title: 'Current',
|
||||
value: _valueFromCode('Current', generalDataPoints),
|
||||
value: _valueFromCode('Current', generalDataPoints)
|
||||
.replaceAllMapped(
|
||||
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
|
||||
(Match m) => '${m[1]}.',
|
||||
)
|
||||
.replaceAll('.0', ''),
|
||||
unit: 'A',
|
||||
),
|
||||
PowerClampEnergyStatus(
|
||||
|
@ -125,7 +125,48 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
||||
(e) => e.code == code,
|
||||
orElse: () => DataPoint(value: '--'),
|
||||
);
|
||||
final value = element?.value;
|
||||
if (code.contains('Current')) {
|
||||
return _formatCurrentValue(value?.toString());
|
||||
}
|
||||
if (code.contains('PowerFactor')) {
|
||||
return _formatPowerFactor(value?.toString());
|
||||
}
|
||||
if (code.contains('Voltage')) {
|
||||
return _formatVoltage(value?.toString());
|
||||
}
|
||||
return value?.toString() ?? '--';
|
||||
}
|
||||
|
||||
return element?.value.toString() ?? '--';
|
||||
String _formatCurrentValue(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
if (str.length == 1) return '${str[0]}.0';
|
||||
return '${str[0]}.${str.substring(1)}';
|
||||
}
|
||||
|
||||
String _formatPowerFactor(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
final intValue = int.tryParse(str);
|
||||
if (intValue == null) return '--';
|
||||
final doubleValue = intValue / 100;
|
||||
return doubleValue.toStringAsFixed(2);
|
||||
}
|
||||
|
||||
String _formatVoltage(String? value) {
|
||||
if (value == null) return '--';
|
||||
String str = value;
|
||||
if (str.isEmpty || str == '--') return '--';
|
||||
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
if (str.isEmpty) return '--';
|
||||
if (str.length == 1) return '0.${str[0]}';
|
||||
return '${str.substring(0, str.length - 1)}.${str.substring(str.length - 1)}';
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,11 @@ import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||
import 'package:syncrow_web/services/home_api.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
|
||||
class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
// final Graph graph = Graph()..isTree = true;
|
||||
// final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
|
||||
// List<Node> sourcesList = [];
|
||||
// List<Node> destinationsList = [];
|
||||
UserModel? user;
|
||||
String terms = '';
|
||||
String policy = '';
|
||||
@ -33,31 +28,14 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
|
||||
}
|
||||
|
||||
// void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
|
||||
// emit(HomeInitial());
|
||||
// sourcesList.add(event.sourceNode);
|
||||
// destinationsList.add(event.destinationNode);
|
||||
// for (int i = 0; i < sourcesList.length; i++) {
|
||||
// graph.addEdge(sourcesList[i], destinationsList[i]);
|
||||
// }
|
||||
|
||||
// builder
|
||||
// ..siblingSeparation = (100)
|
||||
// ..levelSeparation = (150)
|
||||
// ..subtreeSeparation = (150)
|
||||
// ..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM);
|
||||
// emit(HomeUpdateTree(graph: graph, builder: builder));
|
||||
// }
|
||||
|
||||
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> 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<SpaceTreeBloc>().add(InitialEvent());
|
||||
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
|
||||
}
|
||||
add(FetchTermEvent());
|
||||
add(FetchPolicyEvent());
|
||||
@ -89,12 +67,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future _confirmUserAgreement(
|
||||
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
||||
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> 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) {
|
||||
@ -102,16 +78,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
}
|
||||
}
|
||||
|
||||
// static Future fetchUserInfo() async {
|
||||
// try {
|
||||
// var uuid =
|
||||
// await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||
// user = await HomeApi().fetchUserInfo(uuid);
|
||||
// } catch (e) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
List<HomeItemModel> homeItems = [
|
||||
HomeItemModel(
|
||||
title: 'Access Management',
|
||||
@ -121,7 +87,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||
context.go(RoutesConst.accessManagementPage);
|
||||
},
|
||||
color: null,
|
||||
color: const Color(0xFF0036E6),
|
||||
),
|
||||
HomeItemModel(
|
||||
title: 'Space Management',
|
||||
@ -131,7 +97,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||
context.go(RoutesConst.spacesManagementPage);
|
||||
},
|
||||
color: ColorsManager.primaryColor,
|
||||
color: const Color(0xFF0026A2),
|
||||
),
|
||||
HomeItemModel(
|
||||
title: 'Devices Management',
|
||||
@ -143,12 +109,11 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||
context.go(RoutesConst.deviceManagementPage);
|
||||
},
|
||||
color: ColorsManager.primaryColor,
|
||||
color: const Color(0xFF00165E),
|
||||
),
|
||||
|
||||
HomeItemModel(
|
||||
title: 'Syncrow Analytics',
|
||||
icon: Assets.devicesIcon,
|
||||
icon: Assets.analyticsIcon,
|
||||
active: true,
|
||||
onPress: (context) {
|
||||
context.read<SpaceTreeBloc>().add(ClearCachedData());
|
||||
@ -156,43 +121,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
|
||||
context.go(RoutesConst.analytics);
|
||||
},
|
||||
color: ColorsManager.primaryColor,
|
||||
color: const Color(0xFF023DFE),
|
||||
),
|
||||
|
||||
// HomeItemModel(
|
||||
// title: 'Move in',
|
||||
// icon: Assets.moveinIcon,
|
||||
// active: false,
|
||||
// onPress: (context) {},
|
||||
// color: ColorsManager.primaryColor,
|
||||
// ),
|
||||
// HomeItemModel(
|
||||
// title: 'Construction',
|
||||
// icon: Assets.constructionIcon,
|
||||
// active: false,
|
||||
// onPress: (context) {},
|
||||
// color: ColorsManager.primaryColor,
|
||||
// ),
|
||||
// HomeItemModel(
|
||||
// title: 'Energy',
|
||||
// icon: Assets.energyIcon,
|
||||
// active: false,
|
||||
// onPress: (context) {},
|
||||
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// ),
|
||||
// HomeItemModel(
|
||||
// title: 'Integrations',
|
||||
// icon: Assets.integrationsIcon,
|
||||
// active: false,
|
||||
// onPress: (context) {},
|
||||
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// ),
|
||||
// HomeItemModel(
|
||||
// title: 'Asset',
|
||||
// icon: Assets.assetIcon,
|
||||
// active: false,
|
||||
// onPress: (context) {},
|
||||
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// ),
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class HomeCard extends StatelessWidget {
|
||||
final bool active;
|
||||
@ -8,6 +7,7 @@ class HomeCard extends StatelessWidget {
|
||||
final int index;
|
||||
final String name;
|
||||
final Function()? onTap;
|
||||
final Color? color;
|
||||
const HomeCard({
|
||||
super.key,
|
||||
required this.name,
|
||||
@ -15,28 +15,16 @@ class HomeCard extends StatelessWidget {
|
||||
this.active = false,
|
||||
required this.img,
|
||||
required this.onTap,
|
||||
required this.color,
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// bool evenNumbers = index % 2 == 0;
|
||||
return InkWell(
|
||||
onTap: active ? onTap : null,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: index == 0 && active
|
||||
? ColorsManager.blue1.withOpacity(0.9)
|
||||
: index == 1 && active
|
||||
? ColorsManager.blue2.withOpacity(0.9)
|
||||
: index == 2 && active
|
||||
? ColorsManager.blue3
|
||||
: index == 4 && active == false
|
||||
? ColorsManager.blue4.withOpacity(0.2)
|
||||
: index == 7 && active == false
|
||||
? ColorsManager.blue4.withOpacity(0.2)
|
||||
: ColorsManager.blueColor.withOpacity(0.2),
|
||||
// (active ?ColorsManager.blueColor
|
||||
// : ColorsManager.blueColor.withOpacity(0.2)),
|
||||
color: color,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Column(
|
||||
@ -64,15 +52,9 @@ class HomeCard extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: SvgPicture.asset(
|
||||
img,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: SvgPicture.asset(img),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -50,7 +50,7 @@ class HomeMobilePage extends StatelessWidget {
|
||||
height: size.height * 0.6,
|
||||
width: size.width * 0.68,
|
||||
child: GridView.builder(
|
||||
itemCount: homeItems.length,
|
||||
itemCount: homeBloc.homeItems.length,
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
@ -61,7 +61,8 @@ class HomeMobilePage extends StatelessWidget {
|
||||
itemBuilder: (context, index) {
|
||||
return HomeCard(
|
||||
index: index,
|
||||
active: homeBloc.homeItems[index].active!,
|
||||
active: true,
|
||||
color: homeBloc.homeItems[index].color,
|
||||
name: homeBloc.homeItems[index].title!,
|
||||
img: homeBloc.homeItems[index].icon!,
|
||||
onTap: () =>
|
||||
@ -78,56 +79,4 @@ class HomeMobilePage extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final dynamic homeItems = [
|
||||
{
|
||||
'title': 'Access',
|
||||
'icon': Assets.accessIcon,
|
||||
'active': true,
|
||||
},
|
||||
{
|
||||
'title': 'Space\nManagement',
|
||||
'icon': Assets.spaseManagementIcon,
|
||||
'color': ColorsManager.primaryColor,
|
||||
'active': true,
|
||||
},
|
||||
{
|
||||
'title': 'Devices',
|
||||
'icon': Assets.devicesIcon,
|
||||
'active': true,
|
||||
},
|
||||
{
|
||||
'title': 'Syncrow Analytics',
|
||||
'icon': Assets.iconEdit,
|
||||
'active': true,
|
||||
},
|
||||
// {
|
||||
// 'title': 'Move in',
|
||||
// 'icon': Assets.moveinIcon,
|
||||
// 'active': false,
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Construction',
|
||||
// 'icon': Assets.constructionIcon,
|
||||
// 'active': false,
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Energy',
|
||||
// 'icon': Assets.energyIcon,
|
||||
// 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// 'active': false,
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Integrations',
|
||||
// 'icon': Assets.integrationsIcon,
|
||||
// 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// 'active': false,
|
||||
// },
|
||||
// {
|
||||
// 'title': 'Asset',
|
||||
// 'icon': Assets.assetIcon,
|
||||
// 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||
// 'active': false,
|
||||
// },
|
||||
];
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||
// homeBloc.add(const FetchUserInfo());
|
||||
homeBloc.add(const FetchUserInfo());
|
||||
}
|
||||
|
||||
@override
|
||||
@ -38,10 +38,8 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
||||
child: BlocConsumer<HomeBloc, HomeState>(
|
||||
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,
|
||||
@ -56,7 +54,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
||||
_dialogShown = false;
|
||||
if (v != null) {
|
||||
homeBloc.add(ConfirmUserAgreementEvent());
|
||||
// homeBloc.add(const FetchUserInfo());
|
||||
homeBloc.add(const FetchUserInfo());
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -100,8 +98,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
||||
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,
|
||||
@ -109,12 +106,12 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return HomeCard(
|
||||
color: homeBloc.homeItems[index].color,
|
||||
index: index,
|
||||
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),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -21,8 +21,7 @@ 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<SpaceManagementEvent, SpaceManagementState> {
|
||||
class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||
final CommunitySpaceManagementApi _api;
|
||||
final ProductApi _productApi;
|
||||
final SpaceModelManagementApi _spaceModelApi;
|
||||
@ -63,8 +62,7 @@ class SpaceManagementBloc
|
||||
int page = 1;
|
||||
|
||||
while (hasNext) {
|
||||
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||
page: page, projectId: projectUuid);
|
||||
final spaceModels = await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
||||
if (spaceModels.isNotEmpty) {
|
||||
allSpaceModels.addAll(spaceModels);
|
||||
page++;
|
||||
@ -77,29 +75,26 @@ class SpaceManagementBloc
|
||||
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 ?? []));
|
||||
}
|
||||
|
||||
void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event,
|
||||
Emitter<SpaceManagementState> emit) async {
|
||||
void _deleteSpaceModelFromCache(
|
||||
DeleteSpaceModelFromCache event, Emitter<SpaceManagementState> 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 ?? []));
|
||||
@ -127,8 +122,8 @@ class SpaceManagementBloc
|
||||
int page = 1;
|
||||
|
||||
while (hasNext) {
|
||||
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||
page: page, projectId: projectUuid);
|
||||
final spaceModels =
|
||||
await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
||||
if (spaceModels.isNotEmpty) {
|
||||
allSpaceModels.addAll(spaceModels);
|
||||
page++;
|
||||
@ -169,12 +164,10 @@ class SpaceManagementBloc
|
||||
await fetchTags();
|
||||
|
||||
emit(SpaceManagementLoading());
|
||||
final success = await _api.updateCommunity(
|
||||
event.communityUuid, event.name, projectUuid);
|
||||
final success = await _api.updateCommunity(event.communityUuid, event.name, projectUuid);
|
||||
if (success) {
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
final updatedCommunities =
|
||||
List<CommunityModel>.from(previousState.communities);
|
||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities);
|
||||
for (var community in updatedCommunities) {
|
||||
if (community.uuid == event.communityUuid) {
|
||||
community.name = event.name;
|
||||
@ -219,8 +212,7 @@ class SpaceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(
|
||||
String communityUuid) async {
|
||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(String communityUuid) async {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
|
||||
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
||||
@ -250,23 +242,20 @@ class SpaceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onBlankState(
|
||||
BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
Future<void> _onBlankState(BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
try {
|
||||
final previousState = state;
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||
|
||||
List<CommunityModel> communities =
|
||||
await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||
await fetchSpaceModels();
|
||||
// await fetchTags();
|
||||
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<CommunityModel>.from(prevCommunities),
|
||||
@ -297,8 +286,7 @@ class SpaceManagementBloc
|
||||
_onloadProducts();
|
||||
await fetchTags();
|
||||
// Wait until `communityList` is loaded
|
||||
List<CommunityModel> communities =
|
||||
await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||
|
||||
// Fetch space models after communities are available
|
||||
final prevSpaceModels = await fetchSpaceModels();
|
||||
@ -322,9 +310,8 @@ class SpaceManagementBloc
|
||||
final completer = Completer<List<CommunityModel>>();
|
||||
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 {
|
||||
@ -352,8 +339,7 @@ class SpaceManagementBloc
|
||||
emit(SpaceManagementLoading());
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
|
||||
final success =
|
||||
await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||
final success = await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||
if (success) {
|
||||
// add(LoadCommunityAndSpacesEvent());
|
||||
} else {
|
||||
@ -375,13 +361,12 @@ class SpaceManagementBloc
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await fetchTags();
|
||||
CommunityModel? newCommunity = await _api.createCommunity(
|
||||
event.name, event.description, projectUuid);
|
||||
CommunityModel? newCommunity =
|
||||
await _api.createCommunity(event.name, event.description, projectUuid);
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
if (newCommunity != null) {
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState) {
|
||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
||||
final prevCommunities = List<CommunityModel>.from(
|
||||
(previousState as dynamic).communities,
|
||||
);
|
||||
@ -474,15 +459,12 @@ class SpaceManagementBloc
|
||||
try {
|
||||
final spaceTreeState = event.context.read<SpaceTreeBloc>().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));
|
||||
// updatedSpaces.forEach(
|
||||
// (element) => element.uuid,
|
||||
// );
|
||||
// final lastUpdatedSpaced = updatedSpaces..addAll(allSpaces);
|
||||
emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
||||
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
await _updateLoadedState(
|
||||
spaceTreeState,
|
||||
@ -493,7 +475,7 @@ class SpaceManagementBloc
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
// emit(SpaceManagementError('Error saving spaces: $e'));
|
||||
emit(SpaceManagementError('Error saving spaces: $e'));
|
||||
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
emit(previousState);
|
||||
@ -533,15 +515,13 @@ class SpaceManagementBloc
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit(previousState);
|
||||
} catch (e, stackTrace) {
|
||||
emit(previousState);
|
||||
// rethrow;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> saveSpacesHierarchically(BuildContext context,
|
||||
List<SpaceModel> spaces, String communityUuid) async {
|
||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
||||
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
||||
final orderedSpaces = flattenHierarchy(spaces);
|
||||
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
@ -554,14 +534,6 @@ 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 [];
|
||||
@ -576,7 +548,9 @@ class SpaceManagementBloc
|
||||
if (parent.uuid != null) {
|
||||
await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid);
|
||||
}
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
orderedSpaces.removeWhere((space) => parentsToDelete.contains(space));
|
||||
|
||||
@ -590,7 +564,7 @@ class SpaceManagementBloc
|
||||
|
||||
if (matchedSpaces.isEmpty) continue;
|
||||
|
||||
final prevSpace = matchedSpaces.elementAtOrNull(0);
|
||||
final prevSpace = matchedSpaces[0];
|
||||
|
||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
||||
@ -601,19 +575,17 @@ class SpaceManagementBloc
|
||||
if (prevSubspaces != null || newSubspaces != null) {
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final existsInNew = newSubspaces
|
||||
.any((subspace) => 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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -641,9 +613,7 @@ class SpaceManagementBloc
|
||||
}
|
||||
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
final newSubspaceMap = {
|
||||
for (var subspace in newSubspaces) subspace.uuid: subspace
|
||||
};
|
||||
final newSubspaceMap = {for (var subspace in newSubspaces) subspace.uuid: subspace};
|
||||
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||
@ -669,10 +639,9 @@ class SpaceManagementBloc
|
||||
isPrivate: space.isPrivate,
|
||||
position: space.position,
|
||||
icon: space.icon,
|
||||
subspaces: space.subspaces,
|
||||
// subspaceUpdates,
|
||||
tags: space.tags,
|
||||
// tagUpdates,
|
||||
subspaces: subspaceUpdates,
|
||||
tags: tagUpdates,
|
||||
direction: space.incomingConnection?.direction,
|
||||
spaceModelUuid: space.spaceModel?.uuid,
|
||||
projectId: projectUuid);
|
||||
} else {
|
||||
@ -682,10 +651,8 @@ class SpaceManagementBloc
|
||||
: [];
|
||||
|
||||
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||
final tagBodyModels = subspace.tags
|
||||
?.map((tag) => tag.toCreateTagBodyModel())
|
||||
.toList() ??
|
||||
[];
|
||||
final tagBodyModels =
|
||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
||||
return CreateSubspaceModel()
|
||||
..subspaceName = subspace.subspaceName
|
||||
..tags = tagBodyModels;
|
||||
@ -704,6 +671,7 @@ class SpaceManagementBloc
|
||||
isPrivate: space.isPrivate,
|
||||
position: space.position,
|
||||
icon: space.icon,
|
||||
direction: space.incomingConnection?.direction,
|
||||
spaceModelUuid: space.spaceModel?.uuid,
|
||||
tags: tagBodyModels,
|
||||
subspaces: createSubspaceBodyModels,
|
||||
@ -711,8 +679,7 @@ class SpaceManagementBloc
|
||||
space.uuid = response?.uuid;
|
||||
}
|
||||
} catch (e) {
|
||||
return [];
|
||||
// Stop further execution on failure
|
||||
rethrow; // Stop further execution on failure
|
||||
}
|
||||
}
|
||||
return spaces;
|
||||
@ -743,8 +710,7 @@ class SpaceManagementBloc
|
||||
return result.toList(); // Convert back to a list
|
||||
}
|
||||
|
||||
void _onLoadSpaceModel(
|
||||
SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
void _onLoadSpaceModel(SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
@ -791,17 +757,14 @@ class SpaceManagementBloc
|
||||
// Case 1: Tags deleted
|
||||
if (prevTags != null && newTags != null) {
|
||||
for (var prevTag in prevTags) {
|
||||
final existsInNew =
|
||||
newTags.any((newTag) => 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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -844,16 +807,15 @@ class SpaceManagementBloc
|
||||
return tagUpdates;
|
||||
}
|
||||
|
||||
List<SpaceModel> findMatchingSpaces(
|
||||
List<SpaceModel> spaces, String targetUuid) {
|
||||
List<SpaceModel> findMatchingSpaces(List<SpaceModel> spaces, String targetUuid) {
|
||||
List<SpaceModel> 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;
|
||||
|
@ -3,26 +3,23 @@ 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,
|
||||
});
|
||||
Connection({required this.startSpace, required this.endSpace, required this.direction});
|
||||
|
||||
Map<String, dynamic> 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
|
||||
'startUuid': startSpace.uuid ?? 'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces
|
||||
'endUuid': endSpace.uuid ?? 'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces
|
||||
'direction': direction,
|
||||
};
|
||||
}
|
||||
|
||||
static Connection fromMap(
|
||||
Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
||||
static Connection fromMap(Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
||||
return Connection(
|
||||
startSpace: spaces[map['startUuid']]!,
|
||||
endSpace: spaces[map['endUuid']]!,
|
||||
direction: map['direction'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
import 'selected_product_model.dart';
|
||||
|
||||
class ProductModel {
|
||||
final String uuid;
|
||||
final String catName;
|
||||
@ -40,15 +38,6 @@ 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,
|
||||
|
@ -101,7 +101,7 @@ class SpaceModel {
|
||||
spaceModel: json['spaceModel'] != null
|
||||
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
||||
: null,
|
||||
tags: (json['productAllocations'] as List<dynamic>?)
|
||||
tags: (json['tags'] as List<dynamic>?)
|
||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
||||
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
@ -116,6 +116,7 @@ class SpaceModel {
|
||||
instance.incomingConnection = Connection(
|
||||
startSpace: instance.parent ?? instance, // Parent space
|
||||
endSpace: instance, // This space instance
|
||||
direction: conn['direction'],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class SubspaceModel {
|
||||
subspaceName: json['subspaceName'] ?? '',
|
||||
disabled: json['disabled'] ?? false,
|
||||
internalId: internalId,
|
||||
tags: (json['productAllocations'] as List<dynamic>?)
|
||||
tags: (json['tags'] as List<dynamic>?)
|
||||
?.map((item) => Tag.fromJson(item))
|
||||
.toList() ??
|
||||
[],
|
||||
@ -36,7 +36,7 @@ class SubspaceModel {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
if (uuid != null) 'uuid': uuid,
|
||||
'uuid': uuid,
|
||||
'subspaceName': subspaceName,
|
||||
'disabled': disabled,
|
||||
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
||||
|
@ -23,13 +23,10 @@ class Tag extends BaseTag {
|
||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||
|
||||
return Tag(
|
||||
//TODO:insure UUId for tag or prodAlloc
|
||||
uuid: json['name'] != null ? json['uuid'] : json['tag']?['uuid'] ?? '',
|
||||
uuid: json['uuid'] ?? '',
|
||||
internalId: internalId,
|
||||
tag: json['name'] ?? json['tag']?['name'] ?? '',
|
||||
product: json['product'] != null
|
||||
? ProductModel.fromMap(json['product'])
|
||||
: null,
|
||||
tag: json['name'] ?? '',
|
||||
product: json['product'] != null ? ProductModel.fromMap(json['product']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@ -52,10 +49,9 @@ class Tag extends BaseTag {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
if (uuid != null) 'uuid': uuid,
|
||||
'name': tag,
|
||||
'productUuid': product?.uuid,
|
||||
// .toMap(),
|
||||
'uuid': uuid,
|
||||
'tag': tag,
|
||||
'product': product?.toMap(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -67,8 +67,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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 ?? '',
|
||||
@ -97,15 +96,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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!);
|
||||
});
|
||||
@ -188,8 +185,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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 &&
|
||||
@ -199,11 +195,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
top: entry.value.position.dy,
|
||||
child: SpaceCardWidget(
|
||||
index: entry.key,
|
||||
onButtonTap: (int index, Offset newPosition) {
|
||||
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);
|
||||
},
|
||||
position: entry.value.position,
|
||||
@ -214,9 +210,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
_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,
|
||||
@ -294,6 +289,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
void _showCreateSpaceDialog(Size screenSize,
|
||||
{Offset? position,
|
||||
int? parentIndex,
|
||||
String? direction,
|
||||
double? canvasWidth,
|
||||
double? canvasHeight,
|
||||
required List<Tag> projectTags}) {
|
||||
@ -303,25 +299,19 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? 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
|
||||
} else {
|
||||
newPosition =
|
||||
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position
|
||||
} else {
|
||||
newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
}
|
||||
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
@ -335,13 +325,14 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
subspaces: subspaces,
|
||||
tags: tags);
|
||||
|
||||
if (parentIndex != null) {
|
||||
if (parentIndex != null && direction != 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;
|
||||
@ -369,21 +360,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
widget.selectedSpace!.name = name;
|
||||
@ -393,8 +379,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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) {
|
||||
@ -425,8 +410,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
Map<String, SpaceModel> 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);
|
||||
@ -463,6 +447,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
Connection(
|
||||
startSpace: parent,
|
||||
endSpace: child,
|
||||
direction: "down",
|
||||
),
|
||||
);
|
||||
|
||||
@ -547,16 +532,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: space),
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
||||
);
|
||||
}
|
||||
|
||||
void _deselectSpace(BuildContext context) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
);
|
||||
}
|
||||
|
||||
@ -726,8 +708,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
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<SpaceModel> duplicatedSubtree = [];
|
||||
void collectSubtree(SpaceModel node) {
|
||||
duplicatedSubtree.add(node);
|
||||
@ -745,6 +726,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
final newConnection = Connection(
|
||||
startSpace: parent,
|
||||
endSpace: duplicated,
|
||||
direction: "down",
|
||||
);
|
||||
connections.add(newConnection);
|
||||
duplicated.incomingConnection = newConnection;
|
||||
@ -757,8 +739,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
}
|
||||
|
||||
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,
|
||||
@ -780,6 +761,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
final newConnection = Connection(
|
||||
startSpace: newSpace,
|
||||
endSpace: duplicatedChild,
|
||||
direction: "down",
|
||||
);
|
||||
connections.add(newConnection);
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
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<Tag>? tags;
|
||||
final List<SubspaceModel>? 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,
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
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),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
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,
|
||||
required this.nameController,
|
||||
});
|
||||
TextEditingController nameController;
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,101 +0,0 @@
|
||||
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<SubspaceModel>? subspaces;
|
||||
final void Function() onPressed;
|
||||
|
||||
@override
|
||||
State<SubSpacePartWidget> createState() => _SubSpacePartWidgetState();
|
||||
}
|
||||
|
||||
class _SubSpacePartWidgetState extends State<SubSpacePartWidget> {
|
||||
@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,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -30,13 +30,28 @@ class CurvedLinePainter extends CustomPainter {
|
||||
Offset end = connection.endSpace.position +
|
||||
const Offset(75, 0); // Center top of end space
|
||||
|
||||
// Curved line for down connections
|
||||
final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50);
|
||||
final path = Path()
|
||||
..moveTo(start.dx, start.dy)
|
||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
|
||||
canvas.drawPath(path, paint);
|
||||
if (connection.direction == '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
|
||||
|
||||
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
|
||||
|
@ -1,4 +1,6 @@
|
||||
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';
|
||||
@ -7,11 +9,6 @@ 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';
|
||||
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';
|
||||
@ -19,6 +16,8 @@ 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';
|
||||
@ -83,10 +82,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
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 = [];
|
||||
@ -99,15 +96,13 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
|
||||
@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,
|
||||
@ -117,10 +112,50 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: IconChoosePartWidget(
|
||||
selectedIcon: selectedIcon,
|
||||
showIconSelection: _showIconSelectionDialog,
|
||||
screenWidth: screenWidth,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
@ -129,146 +164,342 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SpaceNameTextfieldWidget(
|
||||
isNameFieldExist: isNameFieldExist,
|
||||
isNameFieldInvalid: isNameFieldInvalid,
|
||||
nameController: nameController,
|
||||
screenWidth: screenWidth,
|
||||
onChange: (value) {
|
||||
enteredName = value.trim();
|
||||
setState(() {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = false;
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
onChanged: (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// 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,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
const SizedBox(height: 25),
|
||||
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),
|
||||
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: 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,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
onTextButtonPressed: () {
|
||||
isTagsAndSubspaceModelDisabled
|
||||
? null
|
||||
: _showTagCreateDialog(
|
||||
context,
|
||||
enteredName,
|
||||
widget.isEdit,
|
||||
widget.products,
|
||||
);
|
||||
},
|
||||
)
|
||||
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 ?? [];
|
||||
})),
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
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 Space',
|
||||
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);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
child: ButtonContentWidget(
|
||||
icon: Icons.add,
|
||||
label: 'Add Devices',
|
||||
disabled: isTagsAndSubspaceModelDisabled,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -298,32 +529,17 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
} else if (isNameFieldExist) {
|
||||
return;
|
||||
} else {
|
||||
String newName = enteredName.isNotEmpty
|
||||
? enteredName
|
||||
: (widget.name ?? '');
|
||||
String newName = enteredName.isNotEmpty ? 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,
|
||||
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'),
|
||||
),
|
||||
@ -334,7 +550,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
//dialooogggs
|
||||
void _showIconSelectionDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -371,50 +586,26 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showSubSpaceDialog(
|
||||
BuildContext context,
|
||||
String name,
|
||||
final List<Tag>? spaceTags,
|
||||
bool isEdit,
|
||||
List<ProductModel>? products,
|
||||
final List<SubspaceModel>? existingSubSpaces,
|
||||
) {
|
||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CreateSubSpaceDialog(
|
||||
spaceName: name,
|
||||
dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces',
|
||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
||||
products: products,
|
||||
existingSubSpaces: existingSubSpaces,
|
||||
onSave: (slectedSubspaces, updatedSubSpaces) {
|
||||
onSave: (slectedSubspaces) {
|
||||
final List<Tag> tagsToAppendToSpace = [];
|
||||
|
||||
if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) {
|
||||
final updatedIds =
|
||||
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||
if (slectedSubspaces != null) {
|
||||
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) {
|
||||
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!);
|
||||
}
|
||||
}
|
||||
@ -432,16 +623,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||
List<ProductModel>? products) {
|
||||
void _showTagCreateDialog(
|
||||
BuildContext context, String name, bool isEdit, List<ProductModel>? 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,
|
||||
@ -456,8 +646,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -481,8 +670,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
allTags: widget.allTags,
|
||||
projectTags: widget.projectTags,
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
@ -492,8 +680,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ class PlusButtonWidget extends StatelessWidget {
|
||||
final int index;
|
||||
final String direction;
|
||||
final Offset offset;
|
||||
final Function(int index, Offset newPosition) onButtonTap;
|
||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
||||
|
||||
const PlusButtonWidget({
|
||||
super.key,
|
||||
@ -17,21 +17,35 @@ class PlusButtonWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onButtonTap(index, const Offset(0, 150));
|
||||
},
|
||||
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 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),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -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) onButtonTap;
|
||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
||||
final Widget Function(int index) buildSpaceContainer;
|
||||
final ValueChanged<Offset> onPositionChanged;
|
||||
|
||||
@ -25,34 +25,35 @@ class SpaceCardWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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,
|
||||
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);
|
||||
},
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
clipBehavior: Clip
|
||||
.none, // Allow hovering elements to be displayed outside the boundary
|
||||
children: [
|
||||
// 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,
|
||||
),
|
||||
buildSpaceContainer(index), // Build the space container
|
||||
if (isHovered) ...[
|
||||
PlusButtonWidget(
|
||||
index: index,
|
||||
direction: 'down',
|
||||
offset: const Offset(63, 50),
|
||||
onButtonTap: onButtonTap,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -13,8 +13,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final existingTagCounts = <String, int>{};
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,17 +22,14 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
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(
|
||||
@ -89,8 +85,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final tags = List<Tag>.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);
|
||||
|
||||
@ -122,8 +117,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags)
|
||||
..remove(event.tagToDelete);
|
||||
final tags = List<Tag>.from(currentState.tags)..remove(event.tagToDelete);
|
||||
|
||||
// Recalculate available tags
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
@ -147,10 +141,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
|
||||
// Get validation error for duplicate tags
|
||||
String? _getValidationError(List<Tag> 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<String, int>>({}, (map, tag) {
|
||||
@ -176,11 +168,9 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
.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 projectTags;
|
||||
// availableTags;
|
||||
return availableTags;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
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';
|
||||
@ -7,10 +12,9 @@ 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 'widgets/save_add_device_row_widget.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class AssignTagDialog extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
@ -25,7 +29,7 @@ class AssignTagDialog extends StatelessWidget {
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const AssignTagDialog(
|
||||
{super.key,
|
||||
{Key? key,
|
||||
required this.products,
|
||||
required this.subspaces,
|
||||
required this.addedProducts,
|
||||
@ -35,14 +39,13 @@ class AssignTagDialog extends StatelessWidget {
|
||||
required this.spaceName,
|
||||
required this.title,
|
||||
this.onSave,
|
||||
required this.projectTags});
|
||||
required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<String> locations = (subspaces ?? [])
|
||||
.map((subspace) => subspace.subspaceName)
|
||||
.toList()
|
||||
..add('Main Space');
|
||||
final List<String> locations =
|
||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AssignTagBloc(projectTags)
|
||||
@ -64,31 +67,131 @@ class AssignTagDialog extends StatelessWidget {
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
AssignTagsTable(
|
||||
controllers: controllers,
|
||||
locations: locations,
|
||||
tags: state.tags,
|
||||
updatedTags: state.updatedTags,
|
||||
onDeleteDevice: ({required index, required tag}) {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(DeleteTag(tagToDelete: tag, tags: state.tags));
|
||||
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];
|
||||
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
onTagDropDownSelected: ({required index, required tag}) {
|
||||
context.read<AssignTagBloc>().add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: tag,
|
||||
));
|
||||
},
|
||||
onLocationDropDownSelected: (
|
||||
{required index, required location}) {
|
||||
context.read<AssignTagBloc>().add(UpdateLocation(
|
||||
index: index,
|
||||
location: location,
|
||||
));
|
||||
},
|
||||
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<AssignTagBloc>().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<AssignTagBloc>().add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: value,
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: DialogDropdown(
|
||||
items: locations,
|
||||
selectedValue: tag.location ?? 'Main Space',
|
||||
onSelected: (value) {
|
||||
context.read<AssignTagBloc>().add(UpdateLocation(
|
||||
index: index,
|
||||
location: value,
|
||||
));
|
||||
},
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
if (state.errorMessage != null)
|
||||
Text(state.errorMessage!,
|
||||
@ -100,15 +203,69 @@ class AssignTagDialog extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
SaveAddDeviceRowWidget(
|
||||
subspaces: subspaces,
|
||||
products: products,
|
||||
projectTags: projectTags,
|
||||
spaceName: spaceName,
|
||||
onSave: onSave,
|
||||
allTags: allTags,
|
||||
tags: state.tags,
|
||||
isSaveEnabled: state.isSaveEnabled,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (buttonContext) => CancelButton(
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
||||
|
||||
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<Tag>.from(state.tags);
|
||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
||||
onSave?.call(processedTags, processedSubspaces);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,100 +0,0 @@
|
||||
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<Tag> tags;
|
||||
final List<SubspaceModel>? subspaces;
|
||||
final List<ProductModel>? products;
|
||||
final List<Tag> projectTags;
|
||||
final String spaceName;
|
||||
final Function(List<Tag> p1, List<SubspaceModel>? p2)? onSave;
|
||||
final List<String>? 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<Tag>.from(tags);
|
||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
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<Tag>.from(tags);
|
||||
final result =
|
||||
TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
onSave?.call(processedTags, processedSubspaces);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
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';
|
||||
@ -8,10 +12,11 @@ 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 'widgets/RowOfCancelSaveWidget.dart';
|
||||
import 'widgets/assign_tags_tables_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class AssignTagModelsDialog extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
@ -48,10 +53,8 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<String> locations = (subspaces ?? [])
|
||||
.map((subspace) => subspace.subspaceName)
|
||||
.toList()
|
||||
..add('Main Space');
|
||||
final List<String> locations =
|
||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AssignTagModelBloc(projectTags)
|
||||
@ -75,38 +78,137 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
AssignTagsTable(
|
||||
controllers: controllers,
|
||||
locations: locations,
|
||||
tags: state.tags,
|
||||
updatedTags: state.updatedTags,
|
||||
onDeleteDevice: ({required index, required tag}) {
|
||||
context
|
||||
.read<AssignTagModelBloc>()
|
||||
.add(DeleteTagModel(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags,
|
||||
));
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
onTagDropDownSelected: (
|
||||
{required index, required tag}) {
|
||||
context.read<AssignTagModelBloc>().add(
|
||||
UpdateTag(
|
||||
index: index,
|
||||
tag: tag,
|
||||
),
|
||||
);
|
||||
},
|
||||
onLocationDropDownSelected: (
|
||||
{required index, required location}) {
|
||||
context.read<AssignTagModelBloc>().add(
|
||||
UpdateLocation(
|
||||
index: index,
|
||||
location: location,
|
||||
),
|
||||
);
|
||||
},
|
||||
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<AssignTagModelBloc>().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<AssignTagModelBloc>().add(UpdateTag(
|
||||
index: index,
|
||||
tag: value,
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: DialogDropdown(
|
||||
items: locations,
|
||||
selectedValue: tag.location ?? 'Main Space',
|
||||
onSelected: (value) {
|
||||
context
|
||||
.read<AssignTagModelBloc>()
|
||||
.add(UpdateLocation(
|
||||
index: index,
|
||||
location: value,
|
||||
));
|
||||
},
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
if (state.errorMessage != null)
|
||||
Text(state.errorMessage!,
|
||||
@ -118,16 +220,101 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
RowOfSaveCancelWidget(
|
||||
subspaces: subspaces,
|
||||
products: products,
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
pageContext: pageContext,
|
||||
projectTags: projectTags,
|
||||
spaceModel: spaceModel,
|
||||
allSpaceModels: allSpaceModels,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (buttonContext) => CancelButton(
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await showDialog<bool>(
|
||||
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<Tag>.from(state.tags);
|
||||
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
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),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -1,152 +0,0 @@
|
||||
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<SubspaceTemplateModel>? subspaces;
|
||||
final List<ProductModel>? products;
|
||||
final List<String>? allTags;
|
||||
final String spaceName;
|
||||
final List<String>? otherSpaceModels;
|
||||
final BuildContext? pageContext;
|
||||
final List<Tag> projectTags;
|
||||
final SpaceTemplateModel? spaceModel;
|
||||
final List<SpaceTemplateModel>? allSpaceModels;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AssignTagModelBloc, AssignTagModelState>(
|
||||
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<Tag>.from(state.tags);
|
||||
final result = TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await showDialog<bool>(
|
||||
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<Tag>.from(state.tags);
|
||||
|
||||
final result = TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
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();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
import 'package:flutter/material.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';
|
||||
|
||||
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<Tag> tags;
|
||||
final List<Tag> updatedTags;
|
||||
final List<TextEditingController> controllers;
|
||||
final List<String> locations;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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: () {
|
||||
onDeleteDevice(tag: tag, index: 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: 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);
|
||||
},
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -77,7 +77,7 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||
emit(SubSpaceState(
|
||||
updatedSubSpaces,
|
||||
updatedSubspaceModels,
|
||||
errorMessage ,
|
||||
errorMessage,
|
||||
updatedDuplicates,
|
||||
));
|
||||
});
|
||||
|
@ -1,23 +1,23 @@
|
||||
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';
|
||||
|
||||
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<SubspaceModel>? existingSubSpaces;
|
||||
final String? spaceName;
|
||||
final List<ProductModel>? products;
|
||||
final void Function(
|
||||
List<SubspaceModel>?, List<UpdateSubspaceModel> updatedSubSpaces)? onSave;
|
||||
final void Function(List<SubspaceModel>?)? onSave;
|
||||
|
||||
const CreateSubSpaceDialog({
|
||||
required this.dialogTitle,
|
||||
@ -33,7 +33,14 @@ class CreateSubSpaceDialog extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
final TextEditingController _subspaceNameController = TextEditingController();
|
||||
late final TextEditingController _subspaceNameController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_subspaceNameController = TextEditingController();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_subspaceNameController.dispose();
|
||||
@ -72,26 +79,83 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
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),
|
||||
TextFieldSubSpaceDialogWidget(
|
||||
subspaceNameController: _subspaceNameController,
|
||||
subSpaces: state.subSpaces,
|
||||
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<SubSpaceBloc>().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<SubSpaceBloc>().add(
|
||||
AddSubSpace(
|
||||
SubspaceModel(
|
||||
subspaceName: trimmedValue,
|
||||
disabled: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
_subspaceNameController.clear();
|
||||
}
|
||||
},
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (state.errorMessage.isNotEmpty)
|
||||
Padding(
|
||||
@ -104,10 +168,36 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
OkCancelSubSpaceWidget(
|
||||
subspaceNameController: _subspaceNameController,
|
||||
errorMessage: state.errorMessage,
|
||||
onSave: widget.onSave,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: CancelButton(
|
||||
label: 'Cancel',
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
onPressed: state.errorMessage.isEmpty
|
||||
? () {
|
||||
final subSpacesBloc = context.read<SubSpaceBloc>();
|
||||
final subSpaces = subSpacesBloc.state.subSpaces;
|
||||
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'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -1,75 +0,0 @@
|
||||
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<SubspaceModel>?, List<UpdateSubspaceModel> updatedSubSpaces)? 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<SubSpaceBloc>();
|
||||
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, subSpacesBloc.state.updatedSubSpaceModels);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
borderRadius: 10,
|
||||
foregroundColor: errorMessage.isNotEmpty
|
||||
? ColorsManager.whiteColorsWithOpacity
|
||||
: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
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<SubspaceModel> 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<SubSpaceBloc>().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<SubSpaceBloc>().add(
|
||||
AddSubSpace(
|
||||
SubspaceModel(
|
||||
subspaceName: trimmedValue,
|
||||
disabled: false,
|
||||
),
|
||||
),
|
||||
);
|
||||
_subspaceNameController.clear();
|
||||
}
|
||||
},
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -14,11 +14,11 @@ class CenterBodyWidget extends StatelessWidget {
|
||||
if (state is InitialState) {
|
||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
||||
}
|
||||
if (state is CommunityStructureState) {
|
||||
if (state is CommunityStructureState) {
|
||||
context.read<SpaceManagementBloc>().add(BlankStateEvent(context));
|
||||
}
|
||||
|
||||
if (state is SpaceModelState) {
|
||||
if (state is SpaceModelState) {
|
||||
context.read<SpaceManagementBloc>().add(SpaceModelLoadEvent(context));
|
||||
}
|
||||
|
||||
@ -31,19 +31,15 @@ class CenterBodyWidget extends StatelessWidget {
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CenterBodyBloc>()
|
||||
.add(CommunityStructureSelectedEvent());
|
||||
context.read<CenterBodyBloc>().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
|
||||
@ -54,26 +50,26 @@ class CenterBodyWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// context.read<CenterBodyBloc>().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<CenterBodyBloc>().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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -24,22 +24,37 @@ class AddDeviceTypeModelBloc
|
||||
|
||||
if (currentState is AddDeviceModelLoaded) {
|
||||
final existingProduct = currentState.selectedProducts.firstWhere(
|
||||
(p) => p.productId == event.selectedProduct.productId,
|
||||
orElse: () => event.selectedProduct,
|
||||
(p) => p.productId == event.productId,
|
||||
orElse: () => SelectedProduct(
|
||||
productId: event.productId,
|
||||
count: 0,
|
||||
productName: event.productName,
|
||||
product: event.product,
|
||||
),
|
||||
);
|
||||
|
||||
List<SelectedProduct> updatedProducts;
|
||||
|
||||
if (event.selectedProduct.count > 0) {
|
||||
if (event.count > 0) {
|
||||
if (!currentState.selectedProducts.contains(existingProduct)) {
|
||||
updatedProducts = [
|
||||
...currentState.selectedProducts,
|
||||
event.selectedProduct,
|
||||
SelectedProduct(
|
||||
productId: event.productId,
|
||||
count: event.count,
|
||||
productName: event.productName,
|
||||
product: event.product,
|
||||
),
|
||||
];
|
||||
} else {
|
||||
updatedProducts = currentState.selectedProducts.map((p) {
|
||||
if (p.productId == event.selectedProduct.productId) {
|
||||
return event.selectedProduct;
|
||||
if (p.productId == event.productId) {
|
||||
return SelectedProduct(
|
||||
productId: p.productId,
|
||||
count: event.count,
|
||||
productName: p.productName,
|
||||
product: p.product,
|
||||
);
|
||||
}
|
||||
return p;
|
||||
}).toList();
|
||||
@ -47,7 +62,7 @@ class AddDeviceTypeModelBloc
|
||||
} else {
|
||||
// Remove the product if the count is 0
|
||||
updatedProducts = currentState.selectedProducts
|
||||
.where((p) => p.productId != event.selectedProduct.productId)
|
||||
.where((p) => p.productId != event.productId)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
@ -10,15 +10,20 @@ abstract class AddDeviceTypeModelEvent extends Equatable {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
||||
final SelectedProduct selectedProduct;
|
||||
|
||||
UpdateProductCountEvent({required this.selectedProduct});
|
||||
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
||||
final String productId;
|
||||
final int count;
|
||||
final String productName;
|
||||
final ProductModel product;
|
||||
|
||||
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedProduct];
|
||||
List<Object> get props => [productId, count];
|
||||
}
|
||||
|
||||
|
||||
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
||||
final List<Tag> initialTags;
|
||||
final List<SelectedProduct> addedProducts;
|
||||
|
@ -54,10 +54,10 @@ class DeviceTypeTileWidget extends StatelessWidget {
|
||||
onCountChanged: (newCount) {
|
||||
context.read<AddDeviceTypeModelBloc>().add(
|
||||
UpdateProductCountEvent(
|
||||
selectedProduct: product.toSelectedProduct(
|
||||
newCount,
|
||||
),
|
||||
),
|
||||
productId: product.uuid,
|
||||
count: newCount,
|
||||
productName: product.catName,
|
||||
product: product),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -32,13 +32,13 @@ class VisitorPasswordDialog extends StatelessWidget {
|
||||
.stateDialog(
|
||||
context: context,
|
||||
message: 'Password Created Successfully',
|
||||
title: 'Send Success',
|
||||
title: 'Sent Successfully',
|
||||
widgeta: Column(
|
||||
children: [
|
||||
if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
const Text('Failed Devises'),
|
||||
const Text('Failed Devices'),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
height: 50,
|
||||
@ -63,7 +63,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
||||
if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
|
||||
Column(
|
||||
children: [
|
||||
const Text('Success Devises'),
|
||||
const Text('Success Devices'),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
height: 50,
|
||||
@ -95,7 +95,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
||||
visitorBloc.stateDialog(
|
||||
context: context,
|
||||
message: state.message,
|
||||
title: 'Something Wrong',
|
||||
title: 'Something went wrong',
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -22,18 +22,6 @@ class HTTPService {
|
||||
);
|
||||
|
||||
client.interceptors.add(serviceLocator.get<HTTPInterceptor>());
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,22 @@ 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<List<CommunityModel>> fetchCommunities(String projectId,
|
||||
{int page = 1}) async {
|
||||
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
|
||||
try {
|
||||
List<CommunityModel> allCommunities = [];
|
||||
bool hasNext = true;
|
||||
|
||||
while (hasNext) {
|
||||
await HTTPService().get(
|
||||
path: ApiEndpoints.getCommunityList
|
||||
.replaceAll('{projectId}', projectId),
|
||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {
|
||||
'page': page,
|
||||
},
|
||||
@ -60,14 +55,8 @@ 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<dynamic> jsonData = json['data'] ?? [];
|
||||
@ -79,10 +68,7 @@ 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;
|
||||
@ -97,8 +83,7 @@ class CommunitySpaceManagementApi {
|
||||
Future<CommunityModel?> 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']);
|
||||
},
|
||||
@ -110,8 +95,7 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<CommunityModel?> createCommunity(
|
||||
String name, String description, String projectId) async {
|
||||
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().post(
|
||||
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
||||
@ -130,8 +114,7 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateCommunity(
|
||||
String communityId, String name, String projectId) async {
|
||||
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateCommunity
|
||||
@ -168,8 +151,7 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpacesResponse> fetchSpaces(
|
||||
String communityId, String projectId) async {
|
||||
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.listSpaces
|
||||
@ -195,8 +177,7 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SpaceModel?> getSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpace
|
||||
@ -218,6 +199,7 @@ class CommunitySpaceManagementApi {
|
||||
{required String communityId,
|
||||
required String name,
|
||||
String? parentId,
|
||||
String? direction,
|
||||
bool isPrivate = false,
|
||||
required Offset position,
|
||||
String? spaceModelUuid,
|
||||
@ -231,6 +213,7 @@ class CommunitySpaceManagementApi {
|
||||
'isPrivate': isPrivate,
|
||||
'x': position.dx,
|
||||
'y': position.dy,
|
||||
'direction': direction,
|
||||
'icon': icon,
|
||||
};
|
||||
if (parentId != null) {
|
||||
@ -265,10 +248,11 @@ class CommunitySpaceManagementApi {
|
||||
required String name,
|
||||
String? parentId,
|
||||
String? icon,
|
||||
String? direction,
|
||||
bool isPrivate = false,
|
||||
required Offset position,
|
||||
List<Tag>? tags,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<TagModelUpdate>? tags,
|
||||
List<UpdateSubspaceTemplateModel>? subspaces,
|
||||
String? spaceModelUuid,
|
||||
required String projectId}) async {
|
||||
try {
|
||||
@ -277,8 +261,9 @@ class CommunitySpaceManagementApi {
|
||||
'isPrivate': isPrivate,
|
||||
'x': position.dx,
|
||||
'y': position.dy,
|
||||
'direction': direction,
|
||||
'icon': icon,
|
||||
'subspaces': subspaces,
|
||||
'subspace': subspaces,
|
||||
'tags': tags,
|
||||
'spaceModelUuid': spaceModelUuid,
|
||||
};
|
||||
@ -304,8 +289,7 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteSpace(
|
||||
String communityId, String spaceId, String projectId) async {
|
||||
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
|
||||
try {
|
||||
final response = await HTTPService().delete(
|
||||
path: ApiEndpoints.deleteSpace
|
||||
@ -323,17 +307,15 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceHierarchy(
|
||||
String communityId, String projectId) async {
|
||||
Future<List<SpaceModel>> 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;
|
||||
},
|
||||
@ -345,17 +327,15 @@ class CommunitySpaceManagementApi {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
|
||||
{String? communityId, String? projectId}) async {
|
||||
Future<List<SpaceModel>> 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;
|
||||
},
|
||||
);
|
||||
|
@ -1,5 +1,3 @@
|
||||
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';
|
||||
@ -9,23 +7,17 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
class SpaceModelManagementApi {
|
||||
Future<List<SpaceTemplateModel>> listSpaceModels(
|
||||
{required String projectId, int page = 1}) async {
|
||||
try {
|
||||
// final response = await HTTPService().get(
|
||||
// path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
||||
// queryParameters: {'page': page},
|
||||
// expectedResponseModel: (json) {
|
||||
// List<dynamic> jsonData = json['data'];
|
||||
// return jsonData.map((jsonItem) {
|
||||
// return SpaceTemplateModel.fromJson(jsonItem);
|
||||
// }).toList();
|
||||
// },
|
||||
// );
|
||||
return [];
|
||||
// response;
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
return [];
|
||||
}
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
||||
queryParameters: {'page': page},
|
||||
expectedResponseModel: (json) {
|
||||
List<dynamic> jsonData = json['data'];
|
||||
return jsonData.map((jsonItem) {
|
||||
return SpaceTemplateModel.fromJson(jsonItem);
|
||||
}).toList();
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<SpaceTemplateModel?> createSpaceModel(
|
||||
@ -41,8 +33,8 @@ class SpaceModelManagementApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<String?> updateSpaceModel(CreateSpaceTemplateBodyModel spaceModel,
|
||||
String spaceModelUuid, String projectId) async {
|
||||
Future<String?> updateSpaceModel(
|
||||
CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid, String projectId) async {
|
||||
final response = await HTTPService().put(
|
||||
path: ApiEndpoints.updateSpaceModel
|
||||
.replaceAll('{projectId}', projectId)
|
||||
@ -55,8 +47,7 @@ class SpaceModelManagementApi {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<SpaceTemplateModel?> getSpaceModel(
|
||||
String spaceModelUuid, String projectId) async {
|
||||
Future<SpaceTemplateModel?> getSpaceModel(String spaceModelUuid, String projectId) async {
|
||||
final response = await HTTPService().get(
|
||||
path: ApiEndpoints.getSpaceModel
|
||||
.replaceAll('{projectId}', projectId)
|
||||
|
@ -14,13 +14,14 @@ class Assets {
|
||||
static const String rightLine = "assets/images/right_line.png";
|
||||
static const String google = "assets/images/google.svg";
|
||||
static const String facebook = "assets/images/facebook.svg";
|
||||
static const String invisiblePassword =
|
||||
"assets/images/Password_invisible.svg";
|
||||
static const String invisiblePassword = "assets/images/Password_invisible.svg";
|
||||
static const String visiblePassword = "assets/images/password_visible.svg";
|
||||
static const String accessIcon = "assets/images/access_icon.svg";
|
||||
static const String spaseManagementIcon =
|
||||
"assets/images/spase_management_icon.svg";
|
||||
static const String devicesIcon = "assets/images/devices_icon.svg";
|
||||
static const String analyticsIcon = "assets/icons/landing_analytics.svg";
|
||||
|
||||
static const String moveinIcon = "assets/images/movein_icon.svg";
|
||||
static const String constructionIcon = "assets/images/construction_icon.svg";
|
||||
static const String energyIcon = "assets/images/energy_icon.svg";
|
||||
@ -32,8 +33,7 @@ class Assets {
|
||||
static const String emptyTable = "assets/images/empty_table.svg";
|
||||
|
||||
// General assets
|
||||
static const String motionlessDetection =
|
||||
"assets/icons/motionless_detection.svg";
|
||||
static const String motionlessDetection = "assets/icons/motionless_detection.svg";
|
||||
static const String acHeating = "assets/icons/ac_heating.svg";
|
||||
static const String acPowerOff = "assets/icons/ac_power_off.svg";
|
||||
static const String acFanMiddle = "assets/icons/ac_fan_middle.svg";
|
||||
@ -70,22 +70,19 @@ class Assets {
|
||||
"assets/icons/automation_functions/temp_password_unlock.svg";
|
||||
static const String doorlockNormalOpen =
|
||||
"assets/icons/automation_functions/doorlock_normal_open.svg";
|
||||
static const String doorbell =
|
||||
"assets/icons/automation_functions/doorbell.svg";
|
||||
static const String doorbell = "assets/icons/automation_functions/doorbell.svg";
|
||||
static const String remoteUnlockViaApp =
|
||||
"assets/icons/automation_functions/remote_unlock_via_app.svg";
|
||||
static const String doubleLock =
|
||||
"assets/icons/automation_functions/double_lock.svg";
|
||||
static const String selfTestResult =
|
||||
"assets/icons/automation_functions/self_test_result.svg";
|
||||
static const String lockAlarm =
|
||||
"assets/icons/automation_functions/lock_alarm.svg";
|
||||
static const String lockAlarm = "assets/icons/automation_functions/lock_alarm.svg";
|
||||
static const String presenceState =
|
||||
"assets/icons/automation_functions/presence_state.svg";
|
||||
static const String currentTemp =
|
||||
"assets/icons/automation_functions/current_temp.svg";
|
||||
static const String presence =
|
||||
"assets/icons/automation_functions/presence.svg";
|
||||
static const String presence = "assets/icons/automation_functions/presence.svg";
|
||||
static const String residualElectricity =
|
||||
"assets/icons/automation_functions/residual_electricity.svg";
|
||||
static const String hijackAlarm =
|
||||
@ -102,15 +99,12 @@ class Assets {
|
||||
|
||||
// Presence Sensor Assets
|
||||
static const String sensorMotionIcon = "assets/icons/sensor_motion_ic.svg";
|
||||
static const String sensorPresenceIcon =
|
||||
"assets/icons/sensor_presence_ic.svg";
|
||||
static const String sensorPresenceIcon = "assets/icons/sensor_presence_ic.svg";
|
||||
static const String sensorVacantIcon = "assets/icons/sensor_vacant_ic.svg";
|
||||
static const String illuminanceRecordIcon =
|
||||
"assets/icons/illuminance_record_ic.svg";
|
||||
static const String presenceRecordIcon =
|
||||
"assets/icons/presence_record_ic.svg";
|
||||
static const String helpDescriptionIcon =
|
||||
"assets/icons/help_description_ic.svg";
|
||||
static const String presenceRecordIcon = "assets/icons/presence_record_ic.svg";
|
||||
static const String helpDescriptionIcon = "assets/icons/help_description_ic.svg";
|
||||
|
||||
static const String lightPulp = "assets/icons/light_pulb.svg";
|
||||
static const String acDevice = "assets/icons/ac_device.svg";
|
||||
@ -160,12 +154,10 @@ class Assets {
|
||||
static const String unit = 'assets/icons/unit_icon.svg';
|
||||
static const String villa = 'assets/icons/villa_icon.svg';
|
||||
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
|
||||
static const String textFieldSearch =
|
||||
'assets/icons/textfield_search_icon.svg';
|
||||
static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg';
|
||||
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
|
||||
static const String addIcon = 'assets/icons/add_icon.svg';
|
||||
static const String smartThermostatIcon =
|
||||
'assets/icons/smart_thermostat_icon.svg';
|
||||
static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg';
|
||||
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
|
||||
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
|
||||
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
|
||||
@ -213,8 +205,7 @@ class Assets {
|
||||
//assets/icons/water_leak_normal.svg
|
||||
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
|
||||
//assets/icons/water_leak_detected.svg
|
||||
static const String waterLeakDetected =
|
||||
'assets/icons/water_leak_detected.svg';
|
||||
static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg';
|
||||
|
||||
//assets/icons/automation_records.svg
|
||||
static const String automationRecords = 'assets/icons/automation_records.svg';
|
||||
@ -285,16 +276,13 @@ class Assets {
|
||||
"assets/icons/functions_icons/sensitivity.svg";
|
||||
static const String assetsSensitivityOperationIcon =
|
||||
"assets/icons/functions_icons/sesitivity_operation_icon.svg";
|
||||
static const String assetsAcPower =
|
||||
"assets/icons/functions_icons/ac_power.svg";
|
||||
static const String assetsAcPower = "assets/icons/functions_icons/ac_power.svg";
|
||||
static const String assetsAcPowerOFF =
|
||||
"assets/icons/functions_icons/ac_power_off.svg";
|
||||
static const String assetsChildLock =
|
||||
"assets/icons/functions_icons/child_lock.svg";
|
||||
static const String assetsFreezing =
|
||||
"assets/icons/functions_icons/freezing.svg";
|
||||
static const String assetsFanSpeed =
|
||||
"assets/icons/functions_icons/fan_speed.svg";
|
||||
static const String assetsFreezing = "assets/icons/functions_icons/freezing.svg";
|
||||
static const String assetsFanSpeed = "assets/icons/functions_icons/fan_speed.svg";
|
||||
static const String assetsAcCooling =
|
||||
"assets/icons/functions_icons/ac_cooling.svg";
|
||||
static const String assetsAcHeating =
|
||||
@ -303,8 +291,7 @@ class Assets {
|
||||
"assets/icons/functions_icons/celsius_degrees.svg";
|
||||
static const String assetsTempreture =
|
||||
"assets/icons/functions_icons/tempreture.svg";
|
||||
static const String assetsAcFanLow =
|
||||
"assets/icons/functions_icons/ac_fan_low.svg";
|
||||
static const String assetsAcFanLow = "assets/icons/functions_icons/ac_fan_low.svg";
|
||||
static const String assetsAcFanMiddle =
|
||||
"assets/icons/functions_icons/ac_fan_middle.svg";
|
||||
static const String assetsAcFanHigh =
|
||||
@ -323,8 +310,7 @@ class Assets {
|
||||
"assets/icons/functions_icons/far_detection.svg";
|
||||
static const String assetsFarDetectionFunction =
|
||||
"assets/icons/functions_icons/far_detection_function.svg";
|
||||
static const String assetsIndicator =
|
||||
"assets/icons/functions_icons/indicator.svg";
|
||||
static const String assetsIndicator = "assets/icons/functions_icons/indicator.svg";
|
||||
static const String assetsMotionDetection =
|
||||
"assets/icons/functions_icons/motion_detection.svg";
|
||||
static const String assetsMotionlessDetection =
|
||||
@ -337,8 +323,7 @@ class Assets {
|
||||
"assets/icons/functions_icons/master_state.svg";
|
||||
static const String assetsSwitchAlarmSound =
|
||||
"assets/icons/functions_icons/switch_alarm_sound.svg";
|
||||
static const String assetsResetOff =
|
||||
"assets/icons/functions_icons/reset_off.svg";
|
||||
static const String assetsResetOff = "assets/icons/functions_icons/reset_off.svg";
|
||||
|
||||
// Assets for automation_functions
|
||||
static const String assetsCardUnlock =
|
||||
@ -382,14 +367,12 @@ class Assets {
|
||||
static const String activeUser = 'assets/icons/active_user.svg';
|
||||
static const String deActiveUser = 'assets/icons/deactive_user.svg';
|
||||
static const String invitedIcon = 'assets/icons/invited_icon.svg';
|
||||
static const String rectangleCheckBox =
|
||||
'assets/icons/rectangle_check_box.png';
|
||||
static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png';
|
||||
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
|
||||
static const String emptyBox = 'assets/icons/empty_box.png';
|
||||
static const String completeProcessIcon =
|
||||
'assets/icons/compleate_process_icon.svg';
|
||||
static const String currentProcessIcon =
|
||||
'assets/icons/current_process_icon.svg';
|
||||
static const String currentProcessIcon = 'assets/icons/current_process_icon.svg';
|
||||
static const String uncomplete_ProcessIcon =
|
||||
'assets/icons/uncompleate_process_icon.svg';
|
||||
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
|
||||
@ -410,11 +393,9 @@ class Assets {
|
||||
static const String successIcon = 'assets/icons/success_icon.svg';
|
||||
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
|
||||
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
|
||||
static const String scenesPlayIconCheck =
|
||||
'assets/icons/scenesPlayIconCheck.png';
|
||||
static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png';
|
||||
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
|
||||
static const String currentDistanceIcon =
|
||||
'assets/icons/current_distance_icon.svg';
|
||||
static const String currentDistanceIcon = 'assets/icons/current_distance_icon.svg';
|
||||
|
||||
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
|
||||
static const String motionDetectionSensitivityIcon =
|
||||
@ -437,44 +418,29 @@ class Assets {
|
||||
static const String cpsMode4 = 'assets/icons/cps_mode4.svg';
|
||||
static const String closeToMotion = 'assets/icons/close_to_motion.svg';
|
||||
static const String farAwayMotion = 'assets/icons/far_away_motion.svg';
|
||||
static const String communicationFault =
|
||||
'assets/icons/communication_fault.svg';
|
||||
static const String communicationFault = 'assets/icons/communication_fault.svg';
|
||||
static const String radarFault = 'assets/icons/radar_fault.svg';
|
||||
static const String selfTestingSuccess =
|
||||
'assets/icons/self_testing_success.svg';
|
||||
static const String selfTestingFailure =
|
||||
'assets/icons/self_testing_failure.svg';
|
||||
static const String selfTestingTimeout =
|
||||
'assets/icons/self_testing_timeout.svg';
|
||||
static const String selfTestingSuccess = 'assets/icons/self_testing_success.svg';
|
||||
static const String selfTestingFailure = 'assets/icons/self_testing_failure.svg';
|
||||
static const String selfTestingTimeout = 'assets/icons/self_testing_timeout.svg';
|
||||
static const String movingSpeed = 'assets/icons/moving_speed.svg';
|
||||
static const String boundary = 'assets/icons/boundary.svg';
|
||||
static const String motionMeter = 'assets/icons/motion_meter.svg';
|
||||
static const String spatialStaticValue =
|
||||
'assets/icons/spatial_static_value.svg';
|
||||
static const String spatialMotionValue =
|
||||
'assets/icons/spatial_motion_value.svg';
|
||||
static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg';
|
||||
static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg';
|
||||
static const String presenceJudgementThrshold =
|
||||
'assets/icons/presence_judgement_threshold.svg';
|
||||
static const String spaceType = 'assets/icons/space_type.svg';
|
||||
static const String sportsPara = 'assets/icons/sports_para.svg';
|
||||
static const String sensitivityFeature1 =
|
||||
'assets/icons/sensitivity_feature_1.svg';
|
||||
static const String sensitivityFeature2 =
|
||||
'assets/icons/sensitivity_feature_2.svg';
|
||||
static const String sensitivityFeature3 =
|
||||
'assets/icons/sensitivity_feature_3.svg';
|
||||
static const String sensitivityFeature4 =
|
||||
'assets/icons/sensitivity_feature_4.svg';
|
||||
static const String sensitivityFeature5 =
|
||||
'assets/icons/sensitivity_feature_5.svg';
|
||||
static const String sensitivityFeature6 =
|
||||
'assets/icons/sensitivity_feature_6.svg';
|
||||
static const String sensitivityFeature7 =
|
||||
'assets/icons/sensitivity_feature_7.svg';
|
||||
static const String sensitivityFeature8 =
|
||||
'assets/icons/sensitivity_feature_8.svg';
|
||||
static const String sensitivityFeature9 =
|
||||
'assets/icons/sensitivity_feature_9.svg';
|
||||
static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg';
|
||||
static const String sensitivityFeature2 = 'assets/icons/sensitivity_feature_2.svg';
|
||||
static const String sensitivityFeature3 = 'assets/icons/sensitivity_feature_3.svg';
|
||||
static const String sensitivityFeature4 = 'assets/icons/sensitivity_feature_4.svg';
|
||||
static const String sensitivityFeature5 = 'assets/icons/sensitivity_feature_5.svg';
|
||||
static const String sensitivityFeature6 = 'assets/icons/sensitivity_feature_6.svg';
|
||||
static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg';
|
||||
static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg';
|
||||
static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg';
|
||||
static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg';
|
||||
static const String targetConfirmTimeIcon =
|
||||
'assets/icons/target_confirm_time_icon.svg';
|
||||
@ -482,5 +448,5 @@ class Assets {
|
||||
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
|
||||
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
|
||||
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
|
||||
static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg';
|
||||
static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg';
|
||||
}
|
||||
|
Reference in New Issue
Block a user