mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-16 18:16:34 +00:00
Compare commits
78 Commits
1495-calen
...
build_main
Author | SHA1 | Date | |
---|---|---|---|
aa3b79bdaf | |||
0e31a3ea96 | |||
fd192894cd | |||
692c9e7792 | |||
08a9a5c71f | |||
7eb1d5b0b0 | |||
0d5734a236 | |||
e22bab00d9 | |||
d2a2d391e0 | |||
1d30c753f5 | |||
ca02de2093 | |||
8f7bfa984b | |||
8e9278c93c | |||
15d3a05553 | |||
a1d7457065 | |||
c99b32fb81 | |||
321df401fd | |||
ee244fa5ed | |||
1db069e9a5 | |||
cf9bafef4d | |||
2c73dd6c31 | |||
6ec20e2d72 | |||
4feae9ad87 | |||
52046909d5 | |||
fc81555be3 | |||
8967852ca8 | |||
a87e79878b | |||
056e7372e0 | |||
d69d867120 | |||
644fe56478 | |||
766a39f161 | |||
c97dd40b05 | |||
0b65c58947 | |||
e0951aa13d | |||
9e8ebf3768 | |||
b593e75c67 | |||
9eaa367d32 | |||
24372a0618 | |||
8988947694 | |||
ef875ef7dc | |||
94e4fbd5db | |||
302ef36b17 | |||
c508d016c2 | |||
e0ad7855d3 | |||
ecf588cfcb | |||
c9d15d102b | |||
64a29681de | |||
02b07cfdb6 | |||
0a94557eee | |||
4f8d1c4ffd | |||
06b320a75d | |||
000fe70663 | |||
4257f7f0f3 | |||
b2bf3866a9 | |||
a15b5439f0 | |||
fd2a09cada | |||
4c2802acfc | |||
15343be258 | |||
c21842cc6d | |||
4326559e14 | |||
4ded7d5202 | |||
0d45a155e3 | |||
625f737791 | |||
494ae1c941 | |||
f67d0e2912 | |||
17aad13b2a | |||
a849c1dafb | |||
3e3e17019a | |||
b1bae3cb15 | |||
051bf657ed | |||
5191c1e456 | |||
7a073f10aa | |||
900d47faae | |||
e35a7fdc70 | |||
d80f5e1f3a | |||
baaf5111b1 | |||
745205063e | |||
c07b53107e |
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,6 +30,7 @@ migrate_working_dir/
|
|||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
analyzer:
|
analyzer:
|
||||||
errors:
|
errors:
|
||||||
constant_identifier_names: ignore
|
constant_identifier_names: ignore
|
||||||
|
overridden_fields: ignore
|
||||||
include: package:flutter_lints/flutter.yaml
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
|
BIN
assets/images/web_Background.png
Normal file
BIN
assets/images/web_Background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
@ -17,7 +17,8 @@ class TagDialogTextfieldDropdown extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState();
|
_DialogTextfieldDropdownState createState() =>
|
||||||
|
_DialogTextfieldDropdownState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||||
@ -36,6 +37,12 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
|
|
||||||
_focusNode.addListener(() {
|
_focusNode.addListener(() {
|
||||||
if (!_focusNode.hasFocus) {
|
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();
|
_closeDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -43,7 +50,9 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
|
|
||||||
void _filterItems() {
|
void _filterItems() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_filteredItems = widget.items.where((tag) => tag.product?.uuid == widget.product).toList();
|
_filteredItems = widget.items;
|
||||||
|
// .where((tag) => tag.product?.uuid == widget.product)
|
||||||
|
// .toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +121,9 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyMedium
|
.bodyMedium
|
||||||
?.copyWith(color: ColorsManager.textPrimaryColor)),
|
?.copyWith(
|
||||||
|
color: ColorsManager
|
||||||
|
.textPrimaryColor)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_controller.text = tag.tag ?? '';
|
_controller.text = tag.tag ?? '';
|
||||||
widget.onSelected(tag);
|
widget.onSelected(tag);
|
||||||
@ -156,13 +167,15 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
onFieldSubmitted: (value) {
|
onFieldSubmitted: (value) {
|
||||||
final selectedTag = _filteredItems.firstWhere((tag) => tag.tag == value,
|
final selectedTag = _filteredItems.firstWhere(
|
||||||
|
(tag) => tag.tag == value,
|
||||||
orElse: () => Tag(tag: value));
|
orElse: () => Tag(tag: value));
|
||||||
widget.onSelected(selectedTag);
|
widget.onSelected(selectedTag);
|
||||||
_closeDropdown();
|
_closeDropdown();
|
||||||
},
|
},
|
||||||
onTapOutside: (event) {
|
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)));
|
orElse: () => Tag(tag: _controller.text)));
|
||||||
_closeDropdown();
|
_closeDropdown();
|
||||||
},
|
},
|
||||||
|
16
lib/core/network/custom_exceptions.dart
Normal file
16
lib/core/network/custom_exceptions.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
import 'enums.dart';
|
||||||
|
|
||||||
|
class GenericException implements Exception {
|
||||||
|
final ExceptionType type;
|
||||||
|
final String errorMessage;
|
||||||
|
const GenericException(
|
||||||
|
{required this.type, this.errorMessage = "Unknown Error"});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Object?> get props => [type, errorMessage];
|
||||||
|
}
|
36
lib/core/network/dio.dart
Normal file
36
lib/core/network/dio.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
import '../../utils/constants/api_const.dart';
|
||||||
|
|
||||||
|
class DioInstance {
|
||||||
|
Dio? _dio;
|
||||||
|
|
||||||
|
Dio get dio => _dio ?? _instantiate();
|
||||||
|
|
||||||
|
String? baseUrl;
|
||||||
|
|
||||||
|
DioInstance({this.baseUrl});
|
||||||
|
|
||||||
|
Dio _instantiate() {
|
||||||
|
Dio dio = Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: baseUrl ?? '${ApiEndpoints.baseUrl}/',
|
||||||
|
receiveDataWhenStatusError: true,
|
||||||
|
headers: {
|
||||||
|
'content_Type': 'application/json',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
dio.interceptors.add(
|
||||||
|
LogInterceptor(
|
||||||
|
responseHeader: false,
|
||||||
|
requestHeader: false,
|
||||||
|
requestBody: true,
|
||||||
|
responseBody: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return dio;
|
||||||
|
}
|
||||||
|
}
|
3
lib/core/network/end_points.dart
Normal file
3
lib/core/network/end_points.dart
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class EndPoints {
|
||||||
|
static const String fetchCommunities = 'projects/{projectUuid}/communities';
|
||||||
|
}
|
50
lib/core/network/enums.dart
Normal file
50
lib/core/network/enums.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
enum RequestType {
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
delete,
|
||||||
|
put,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExceptionType {
|
||||||
|
notAuthenticated,
|
||||||
|
connectionError,
|
||||||
|
// related to http status code exceptions
|
||||||
|
notAuthorized,
|
||||||
|
notFound,
|
||||||
|
internalServerException,
|
||||||
|
serviceUnavailableException,
|
||||||
|
pageGone,
|
||||||
|
|
||||||
|
// related to bad request status code
|
||||||
|
// related to auth requests
|
||||||
|
invalidCredentials,
|
||||||
|
solutionAlreadySunmitted,
|
||||||
|
invalidValidation,
|
||||||
|
// other
|
||||||
|
other,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExceptionMessage {
|
||||||
|
NOT_AUTHENTICATED,
|
||||||
|
INVALID_CREDENTIALS,
|
||||||
|
The_password_field_must_be_at_least_8_characters,
|
||||||
|
SOLUTION_ALREADY_SUBMITTED,
|
||||||
|
you_are_not_authorized,
|
||||||
|
page_not_found,
|
||||||
|
page_gone,
|
||||||
|
INTERNAL_SERVER_ERROR,
|
||||||
|
service_unavailable,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NotificationsType {
|
||||||
|
payment,
|
||||||
|
transporation,
|
||||||
|
product,
|
||||||
|
zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ImagesType {
|
||||||
|
assets,
|
||||||
|
svg,
|
||||||
|
network,
|
||||||
|
}
|
121
lib/core/network/request.dart
Normal file
121
lib/core/network/request.dart
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
import '../../pages/auth/model/token.dart';
|
||||||
|
import 'custom_exceptions.dart';
|
||||||
|
import 'dio.dart';
|
||||||
|
import 'enums.dart';
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
String endPoint;
|
||||||
|
bool? autherized;
|
||||||
|
bool? isFormData;
|
||||||
|
RequestType? method;
|
||||||
|
Map<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",
|
||||||
|
),
|
||||||
|
};
|
@ -1,13 +1,71 @@
|
|||||||
class AnalyticsDevice {
|
class AnalyticsDevice {
|
||||||
const AnalyticsDevice({required this.name, required this.uuid});
|
const AnalyticsDevice({
|
||||||
|
required this.uuid,
|
||||||
|
required this.name,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.deviceTuyaUuid,
|
||||||
|
this.isActive,
|
||||||
|
this.productDevice,
|
||||||
|
this.spaceUuid,
|
||||||
|
});
|
||||||
|
|
||||||
final String uuid;
|
final String uuid;
|
||||||
final String name;
|
final String name;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
final String? deviceTuyaUuid;
|
||||||
|
final bool? isActive;
|
||||||
|
final ProductDevice? productDevice;
|
||||||
|
final String? spaceUuid;
|
||||||
|
|
||||||
factory AnalyticsDevice.fromJson(Map<String, dynamic> json) {
|
factory AnalyticsDevice.fromJson(Map<String, dynamic> json) {
|
||||||
return AnalyticsDevice(
|
return AnalyticsDevice(
|
||||||
uuid: json['uuid'] as String? ?? '',
|
uuid: json['uuid'] as String,
|
||||||
name: json['name'] as String? ?? '',
|
name: json['name'] as String,
|
||||||
|
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null,
|
||||||
|
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null,
|
||||||
|
deviceTuyaUuid: json['deviceTuyaUuid'] as String?,
|
||||||
|
isActive: json['isActive'] as bool?,
|
||||||
|
productDevice: json['productDevice'] != null
|
||||||
|
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
|
||||||
|
: null,
|
||||||
|
spaceUuid: (json['spaces'] as List<dynamic>?)
|
||||||
|
?.map((e) => e['uuid'])
|
||||||
|
.firstOrNull
|
||||||
|
?.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProductDevice {
|
||||||
|
const ProductDevice({
|
||||||
|
this.uuid,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.catName,
|
||||||
|
this.prodId,
|
||||||
|
this.name,
|
||||||
|
this.prodType,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? uuid;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final DateTime? updatedAt;
|
||||||
|
final String? catName;
|
||||||
|
final String? prodId;
|
||||||
|
final String? name;
|
||||||
|
final String? prodType;
|
||||||
|
|
||||||
|
factory ProductDevice.fromJson(Map<String, dynamic> json) {
|
||||||
|
return ProductDevice(
|
||||||
|
uuid: json['uuid'] as String?,
|
||||||
|
createdAt: json['createdAt'] != null ? DateTime.parse(json['createdAt'] as String) : null,
|
||||||
|
updatedAt: json['updatedAt'] != null ? DateTime.parse(json['updatedAt'] as String) : null,
|
||||||
|
catName: json['catName'] as String?,
|
||||||
|
prodId: json['prodId'] as String?,
|
||||||
|
name: json['name'] as String?,
|
||||||
|
prodType: json['prodType'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,32 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class Occupacy extends Equatable {
|
class Occupacy extends Equatable {
|
||||||
final String date;
|
final DateTime date;
|
||||||
final String occupancy;
|
final String occupancy;
|
||||||
|
final String spaceUuid;
|
||||||
|
final int occupiedSeconds;
|
||||||
|
|
||||||
const Occupacy({required this.date, required this.occupancy});
|
const Occupacy({
|
||||||
|
required this.date,
|
||||||
|
required this.occupancy,
|
||||||
|
required this.spaceUuid,
|
||||||
|
required this.occupiedSeconds,
|
||||||
|
});
|
||||||
|
|
||||||
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
||||||
return Occupacy(
|
return Occupacy(
|
||||||
date: json['date'] as String,
|
date: DateTime.parse(json['event_date'] as String? ?? '${DateTime.now()}'),
|
||||||
occupancy: json['occupancy'] as String,
|
occupancy: (json['occupancy_percentage'] ?? 0).toString(),
|
||||||
|
spaceUuid: json['space_uuid'] as String? ?? '',
|
||||||
|
occupiedSeconds: json['occupied_seconds'] as int? ?? 0,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [date, occupancy];
|
List<Object?> get props => [
|
||||||
|
date,
|
||||||
|
occupancy,
|
||||||
|
spaceUuid,
|
||||||
|
occupiedSeconds,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,66 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class PhasesEnergyConsumption extends Equatable {
|
class PhasesEnergyConsumption extends Equatable {
|
||||||
final int month;
|
final String uuid;
|
||||||
final double phaseA;
|
final DateTime createdAt;
|
||||||
final double phaseB;
|
final DateTime updatedAt;
|
||||||
final double phaseC;
|
final String deviceUuid;
|
||||||
|
final DateTime date;
|
||||||
|
final double energyConsumedKw;
|
||||||
|
final double energyConsumedA;
|
||||||
|
final double energyConsumedB;
|
||||||
|
final double energyConsumedC;
|
||||||
|
|
||||||
const PhasesEnergyConsumption({
|
const PhasesEnergyConsumption({
|
||||||
required this.month,
|
required this.uuid,
|
||||||
required this.phaseA,
|
required this.createdAt,
|
||||||
required this.phaseB,
|
required this.updatedAt,
|
||||||
required this.phaseC,
|
required this.deviceUuid,
|
||||||
|
required this.date,
|
||||||
|
required this.energyConsumedKw,
|
||||||
|
required this.energyConsumedA,
|
||||||
|
required this.energyConsumedB,
|
||||||
|
required this.energyConsumedC,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [month, phaseA, phaseB, phaseC];
|
List<Object?> get props => [
|
||||||
|
uuid,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
deviceUuid,
|
||||||
|
date,
|
||||||
|
energyConsumedKw,
|
||||||
|
energyConsumedA,
|
||||||
|
energyConsumedB,
|
||||||
|
energyConsumedC,
|
||||||
|
];
|
||||||
|
|
||||||
factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) {
|
factory PhasesEnergyConsumption.fromJson(Map<String, dynamic> json) {
|
||||||
return PhasesEnergyConsumption(
|
return PhasesEnergyConsumption(
|
||||||
month: json['month'] as int,
|
uuid: json['uuid'] as String,
|
||||||
phaseA: (json['phaseA'] as num).toDouble(),
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||||
phaseB: (json['phaseB'] as num).toDouble(),
|
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||||
phaseC: (json['phaseC'] as num).toDouble(),
|
deviceUuid: json['deviceUuid'] as String,
|
||||||
|
date: DateTime.parse(json['date'] as String),
|
||||||
|
energyConsumedKw: double.parse(json['energyConsumedKw']),
|
||||||
|
energyConsumedA: double.parse(json['energyConsumedA']),
|
||||||
|
energyConsumedB: double.parse(json['energyConsumedB']),
|
||||||
|
energyConsumedC: double.parse(json['energyConsumedC']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'uuid': uuid,
|
||||||
|
'createdAt': createdAt.toIso8601String(),
|
||||||
|
'updatedAt': updatedAt.toIso8601String(),
|
||||||
|
'deviceUuid': deviceUuid,
|
||||||
|
'date': date.toIso8601String().split('T')[0],
|
||||||
|
'energyConsumedKw': energyConsumedKw.toString(),
|
||||||
|
'energyConsumedA': energyConsumedA.toString(),
|
||||||
|
'energyConsumedB': energyConsumedB.toString(),
|
||||||
|
'energyConsumedC': energyConsumedC.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
) {
|
) {
|
||||||
// Add to space tree bloc first
|
|
||||||
context.read<SpaceTreeBloc>().add(
|
context.read<SpaceTreeBloc>().add(
|
||||||
OnCommunitySelected(
|
OnCommunitySelected(
|
||||||
community.uuid,
|
community.uuid,
|
||||||
@ -69,7 +68,9 @@ class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrateg
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
SpaceModel child,
|
SpaceModel child,
|
||||||
) {
|
) {
|
||||||
// Do nothing else as per original implementation
|
if (child.children.isNotEmpty) {
|
||||||
|
return onSpaceSelected(context, community, child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -14,23 +14,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
) {
|
) {
|
||||||
context.read<SpaceTreeBloc>().add(
|
// Do Nothing
|
||||||
OnCommunitySelected(
|
|
||||||
community.uuid,
|
|
||||||
spaces.isNotEmpty ? [spaces.first] : [],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
|
||||||
if (spaceTreeState.selectedCommunities.contains(community.uuid)) {
|
|
||||||
clearData(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(
|
|
||||||
context,
|
|
||||||
communityId: community.uuid,
|
|
||||||
spaceId: spaces.isNotEmpty ? spaces.first.uuid ?? '' : '',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -40,26 +24,17 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
SpaceModel space,
|
SpaceModel space,
|
||||||
) {
|
) {
|
||||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
final selectedSpacesIds = spaceTreeBloc.state.selectedSpaces;
|
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||||
final isSpaceSelected = selectedSpacesIds.contains(space.uuid);
|
|
||||||
|
|
||||||
if (selectedSpacesIds.isEmpty) {
|
if (isSpaceSelected) {
|
||||||
spaceTreeBloc.add(OnCommunitySelected(community.uuid, [space]));
|
|
||||||
} else if (isSpaceSelected) {
|
|
||||||
spaceTreeBloc.add(const SpaceTreeClearSelectionEvent());
|
|
||||||
} else {
|
|
||||||
spaceTreeBloc
|
|
||||||
..add(const SpaceTreeClearSelectionEvent())
|
|
||||||
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
|
||||||
}
|
|
||||||
|
|
||||||
final spaceTreeState = context.read<SpaceTreeBloc>().state;
|
|
||||||
if (spaceTreeState.selectedCommunities.contains(community.uuid) ||
|
|
||||||
spaceTreeState.selectedSpaces.contains(space.uuid)) {
|
|
||||||
clearData(context);
|
clearData(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spaceTreeBloc
|
||||||
|
..add(const SpaceTreeClearSelectionEvent())
|
||||||
|
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||||
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
context,
|
context,
|
||||||
communityId: community.uuid,
|
communityId: community.uuid,
|
||||||
@ -73,7 +48,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
CommunityModel community,
|
CommunityModel community,
|
||||||
SpaceModel child,
|
SpaceModel child,
|
||||||
) {
|
) {
|
||||||
// Do nothing
|
onSpaceSelected(context, community, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -15,9 +15,9 @@ import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_he
|
|||||||
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart';
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/analytics_devices_service_delagate.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_energy_management_analytics_devices_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_energy_management_analytics_devices_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/analytics_devices/remote_occupancy_analytics_devices_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/fake_energy_consumption_by_phases_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/remote_energy_consumption_by_phases_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/remote_energy_consumption_per_device_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupacy/fake_occupacy_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/occupacy/remote_occupancy_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/occupancy_heat_map/remote_occupancy_heat_map_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/power_clamp_info/remote_power_clamp_info_service.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/realtime_device_service/firebase_realtime_device_service.dart';
|
||||||
@ -57,7 +57,7 @@ class _AnalyticsPageState extends State<AnalyticsPage> {
|
|||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => EnergyConsumptionByPhasesBloc(
|
create: (context) => EnergyConsumptionByPhasesBloc(
|
||||||
FakeEnergyConsumptionByPhasesService(),
|
RemoteEnergyConsumptionByPhasesService(_httpService),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
@ -75,7 +75,11 @@ class _AnalyticsPageState extends State<AnalyticsPage> {
|
|||||||
FirebaseRealtimeDeviceService(),
|
FirebaseRealtimeDeviceService(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
BlocProvider(create: (context) => OccupancyBloc(FakeOccupacyService())),
|
BlocProvider(
|
||||||
|
create: (context) => OccupancyBloc(
|
||||||
|
RemoteOccupancyService(_httpService),
|
||||||
|
),
|
||||||
|
),
|
||||||
BlocProvider(
|
BlocProvider(
|
||||||
create: (context) => OccupancyHeatMapBloc(
|
create: (context) => OccupancyHeatMapBloc(
|
||||||
RemoteOccupancyHeatMapService(_httpService),
|
RemoteOccupancyHeatMapService(_httpService),
|
||||||
|
@ -74,6 +74,7 @@ class AnalyticsPageTabsAndChildren extends StatelessWidget {
|
|||||||
FetchEnergyManagementDataHelper
|
FetchEnergyManagementDataHelper
|
||||||
.loadEnergyManagementData(
|
.loadEnergyManagementData(
|
||||||
context,
|
context,
|
||||||
|
shouldFetchAnalyticsDevices: false,
|
||||||
selectedDate: value,
|
selectedDate: value,
|
||||||
communityId:
|
communityId:
|
||||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
spaceTreeState.selectedCommunities.firstOrNull ??
|
||||||
|
@ -45,7 +45,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Dialog(
|
return Dialog(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsetsDirectional.all(20),
|
padding: const EdgeInsetsDirectional.all(20),
|
||||||
width: 320,
|
width: 320,
|
||||||
@ -121,6 +121,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row _buildYearSelector() {
|
Row _buildYearSelector() {
|
||||||
|
final currentYear = DateTime.now().year;
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
@ -134,17 +135,35 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => setState(() => _currentYear = _currentYear - 1),
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_currentYear = _currentYear - 1;
|
||||||
|
});
|
||||||
|
},
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.chevron_left,
|
Icons.chevron_left,
|
||||||
color: ColorsManager.grey700,
|
color: ColorsManager.grey700,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => setState(() => _currentYear = _currentYear + 1),
|
onPressed: _currentYear < currentYear
|
||||||
icon: const Icon(
|
? () {
|
||||||
|
setState(() {
|
||||||
|
_currentYear = _currentYear + 1;
|
||||||
|
// Clear selected month if it becomes invalid in the new year
|
||||||
|
if (_currentYear == currentYear &&
|
||||||
|
_selectedMonth != null &&
|
||||||
|
_selectedMonth! > DateTime.now().month - 1) {
|
||||||
|
_selectedMonth = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
icon: Icon(
|
||||||
Icons.chevron_right,
|
Icons.chevron_right,
|
||||||
color: ColorsManager.grey700,
|
color: _currentYear < currentYear
|
||||||
|
? ColorsManager.grey700
|
||||||
|
: ColorsManager.grey700.withValues(alpha: 0.3),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -152,11 +171,13 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMonthsGrid() {
|
Widget _buildMonthsGrid() {
|
||||||
|
final currentDate = DateTime.now();
|
||||||
|
final isCurrentYear = _currentYear == currentDate.year;
|
||||||
|
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: 12,
|
itemCount: 12,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
childAspectRatio: 2.5,
|
childAspectRatio: 2.5,
|
||||||
@ -165,25 +186,43 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final isSelected = _selectedMonth == index;
|
final isSelected = _selectedMonth == index;
|
||||||
|
final isFutureMonth = isCurrentYear && index > currentDate.month - 1;
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => setState(() => _selectedMonth = index),
|
onTap: isFutureMonth ? null : () => setState(() => _selectedMonth = index),
|
||||||
child: Container(
|
child: DecoratedBox(
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: const Color(0xFFEDF2F7),
|
||||||
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
borderRadius: BorderRadius.only(
|
||||||
: const Color(0xFFEDF2F7),
|
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
borderRadius:
|
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
|
bottomRight:
|
||||||
|
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Container(
|
||||||
_monthNames[index],
|
alignment: Alignment.center,
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
decoration: BoxDecoration(
|
||||||
fontSize: 12,
|
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.whiteColors
|
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
: isFutureMonth
|
||||||
fontWeight: FontWeight.w500,
|
? ColorsManager.grey700.withValues(alpha: 0.1)
|
||||||
|
: const Color(0xFFEDF2F7),
|
||||||
|
borderRadius:
|
||||||
|
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
_monthNames[index],
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isSelected
|
||||||
|
? ColorsManager.whiteColors
|
||||||
|
: isFutureMonth
|
||||||
|
? ColorsManager.blackColor.withValues(alpha: 0.3)
|
||||||
|
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -20,9 +20,9 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|||||||
late int _currentYear;
|
late int _currentYear;
|
||||||
|
|
||||||
static final years = List.generate(
|
static final years = List.generate(
|
||||||
DateTime.now().year - 2020 + 1,
|
DateTime.now().year - (DateTime.now().year - 5) + 1,
|
||||||
(index) => (2020 + index),
|
(index) => (2020 + index),
|
||||||
);
|
).where((year) => year <= DateTime.now().year).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -33,7 +33,7 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Dialog(
|
return Dialog(
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsetsDirectional.all(20),
|
padding: const EdgeInsetsDirectional.all(20),
|
||||||
width: 320,
|
width: 320,
|
||||||
@ -109,7 +109,6 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: years.length,
|
itemCount: years.length,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
childAspectRatio: 2.5,
|
childAspectRatio: 2.5,
|
||||||
@ -120,23 +119,35 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|||||||
final isSelected = _currentYear == years[index];
|
final isSelected = _currentYear == years[index];
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => setState(() => _currentYear = years[index]),
|
onTap: () => setState(() => _currentYear = years[index]),
|
||||||
child: Container(
|
child: DecoratedBox(
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: const Color(0xFFEDF2F7),
|
||||||
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
borderRadius: BorderRadius.only(
|
||||||
: const Color(0xFFEDF2F7),
|
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
borderRadius:
|
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
|
bottomRight:
|
||||||
|
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Container(
|
||||||
years[index].toString(),
|
alignment: Alignment.center,
|
||||||
style: context.textTheme.titleSmall?.copyWith(
|
decoration: BoxDecoration(
|
||||||
fontSize: 12,
|
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.whiteColors
|
? ColorsManager.vividBlue.withValues(alpha: 0.7)
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
: const Color(0xFFEDF2F7),
|
||||||
fontWeight: FontWeight.w500,
|
borderRadius:
|
||||||
|
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
years[index].toString(),
|
||||||
|
style: context.textTheme.titleSmall?.copyWith(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isSelected
|
||||||
|
? ColorsManager.whiteColors
|
||||||
|
: ColorsManager.blackColor.withValues(alpha: 0.8),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
|
|
||||||
|
|
||||||
abstract final class EnergyConsumptionByPhasesChartHelper {
|
|
||||||
const EnergyConsumptionByPhasesChartHelper._();
|
|
||||||
|
|
||||||
static const fakeData = <PhasesEnergyConsumption>[
|
|
||||||
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
|
|
||||||
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
|
|
||||||
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
|
|
||||||
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
|
|
||||||
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
|
|
||||||
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
|
|
||||||
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
|
|
||||||
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
|
|
||||||
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 80, phaseC: 100),
|
|
||||||
];
|
|
||||||
}
|
|
@ -21,12 +21,13 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
reservedSize: 32,
|
reservedSize: 32,
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
maxIncluded: true,
|
maxIncluded: true,
|
||||||
|
minIncluded: true,
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
padding: const EdgeInsetsDirectional.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
(value + 1).toString(),
|
value.toString(),
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.lightGreyColor,
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -36,7 +37,8 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
leftTitles: AxisTitles(
|
leftTitles: AxisTitles(
|
||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
maxIncluded: true,
|
maxIncluded: false,
|
||||||
|
minIncluded: true,
|
||||||
interval: leftTitlesInterval,
|
interval: leftTitlesInterval,
|
||||||
reservedSize: 110,
|
reservedSize: 110,
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
@ -70,7 +72,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
||||||
return touchedSpots.map((spot) {
|
return touchedSpots.map((spot) {
|
||||||
return LineTooltipItem(
|
return LineTooltipItem(
|
||||||
getToolTipLabel(spot.x + 1, spot.y),
|
getToolTipLabel(spot.x, spot.y),
|
||||||
const TextStyle(
|
const TextStyle(
|
||||||
color: ColorsManager.textPrimaryColor,
|
color: ColorsManager.textPrimaryColor,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@ -91,31 +93,38 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static FlBorderData borderData() {
|
|
||||||
return FlBorderData(
|
|
||||||
show: true,
|
|
||||||
border: const Border.symmetric(
|
|
||||||
horizontal: BorderSide(
|
|
||||||
color: ColorsManager.greyColor,
|
|
||||||
style: BorderStyle.solid,
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FlGridData gridData() {
|
static FlGridData gridData() {
|
||||||
return const FlGridData(
|
return FlGridData(
|
||||||
show: true,
|
show: true,
|
||||||
drawVerticalLine: false,
|
drawVerticalLine: false,
|
||||||
drawHorizontalLine: true,
|
drawHorizontalLine: true,
|
||||||
|
horizontalInterval: 250,
|
||||||
|
getDrawingHorizontalLine: (value) {
|
||||||
|
return FlLine(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
strokeWidth: 1,
|
||||||
|
dashArray: value == 0 ? null : [5, 5],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlBorderData borderData() {
|
||||||
|
return FlBorderData(
|
||||||
|
border: const Border(
|
||||||
|
bottom: BorderSide(
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
show: true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LineTouchData lineTouchData() {
|
static LineTouchData lineTouchData() {
|
||||||
return LineTouchData(
|
return LineTouchData(
|
||||||
handleBuiltInTouches: true,
|
handleBuiltInTouches: true,
|
||||||
touchSpotThreshold: 2,
|
touchSpotThreshold: 16,
|
||||||
touchTooltipData: EnergyManagementChartsHelper.lineTouchTooltipData(),
|
touchTooltipData: EnergyManagementChartsHelper.lineTouchTooltipData(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
required String communityId,
|
required String communityId,
|
||||||
required String spaceId,
|
required String spaceId,
|
||||||
DateTime? selectedDate,
|
DateTime? selectedDate,
|
||||||
|
bool shouldFetchAnalyticsDevices = true,
|
||||||
}) {
|
}) {
|
||||||
if (communityId.isEmpty && spaceId.isEmpty) {
|
if (communityId.isEmpty && spaceId.isEmpty) {
|
||||||
clearAllData(context);
|
clearAllData(context);
|
||||||
@ -34,31 +35,46 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
|
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||||
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
|
final selectedDate0 = selectedDate ?? datePickerState.monthlyDate;
|
||||||
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
if (shouldFetchAnalyticsDevices) {
|
||||||
|
loadAnalyticsDevices(
|
||||||
|
context,
|
||||||
|
communityUuid: communityId,
|
||||||
|
spaceUuid: spaceId,
|
||||||
|
selectedDate: selectedDate0,
|
||||||
|
);
|
||||||
|
loadRealtimeDeviceChanges(context);
|
||||||
|
loadPowerClampInfo(context);
|
||||||
|
}
|
||||||
loadTotalEnergyConsumption(
|
loadTotalEnergyConsumption(
|
||||||
context,
|
context,
|
||||||
selectedDate: selectedDate0,
|
selectedDate: selectedDate0,
|
||||||
communityId: communityId,
|
communityId: communityId,
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
);
|
);
|
||||||
loadEnergyConsumptionByPhases(context, selectedDate: selectedDate);
|
final selectedDevice = getSelectedDevice(context);
|
||||||
|
if (selectedDevice case final AnalyticsDevice device) {
|
||||||
|
loadEnergyConsumptionByPhases(
|
||||||
|
context,
|
||||||
|
powerClampUuid: device.uuid,
|
||||||
|
selectedDate: selectedDate0,
|
||||||
|
);
|
||||||
|
}
|
||||||
loadEnergyConsumptionPerDevice(
|
loadEnergyConsumptionPerDevice(
|
||||||
context,
|
context,
|
||||||
communityId: communityId,
|
communityId: communityId,
|
||||||
spaceId: spaceId,
|
spaceId: spaceId,
|
||||||
selectedDate: selectedDate0,
|
selectedDate: selectedDate0,
|
||||||
);
|
);
|
||||||
loadRealtimeDeviceChanges(context);
|
|
||||||
loadPowerClampInfo(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void loadEnergyConsumptionByPhases(
|
static void loadEnergyConsumptionByPhases(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
required String powerClampUuid,
|
||||||
DateTime? selectedDate,
|
DateTime? selectedDate,
|
||||||
}) {
|
}) {
|
||||||
final param = GetEnergyConsumptionByPhasesParam(
|
final param = GetEnergyConsumptionByPhasesParam(
|
||||||
startDate: selectedDate,
|
date: selectedDate,
|
||||||
spaceId: '',
|
powerClampUuid: powerClampUuid,
|
||||||
);
|
);
|
||||||
context.read<EnergyConsumptionByPhasesBloc>().add(
|
context.read<EnergyConsumptionByPhasesBloc>().add(
|
||||||
LoadEnergyConsumptionByPhasesEvent(param: param),
|
LoadEnergyConsumptionByPhasesEvent(param: param),
|
||||||
@ -121,6 +137,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required String communityUuid,
|
required String communityUuid,
|
||||||
required String spaceUuid,
|
required String spaceUuid,
|
||||||
|
required DateTime selectedDate,
|
||||||
}) {
|
}) {
|
||||||
context.read<AnalyticsDevicesBloc>().add(
|
context.read<AnalyticsDevicesBloc>().add(
|
||||||
LoadAnalyticsDevicesEvent(
|
LoadAnalyticsDevicesEvent(
|
||||||
@ -128,6 +145,11 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
context.read<PowerClampInfoBloc>().add(
|
context.read<PowerClampInfoBloc>().add(
|
||||||
LoadPowerClampInfoEvent(device.uuid),
|
LoadPowerClampInfoEvent(device.uuid),
|
||||||
);
|
);
|
||||||
|
loadEnergyConsumptionByPhases(
|
||||||
|
context,
|
||||||
|
powerClampUuid: device.uuid,
|
||||||
|
selectedDate: selectedDate,
|
||||||
|
);
|
||||||
context.read<RealtimeDeviceChangesBloc>().add(
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
RealtimeDeviceChangesStarted(device.uuid),
|
RealtimeDeviceChangesStarted(device.uuid),
|
||||||
);
|
);
|
||||||
|
@ -48,6 +48,7 @@ class AnalyticsDeviceDropdown extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) {
|
Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) {
|
||||||
|
final spaceUuid = state.selectedDevice?.spaceUuid;
|
||||||
return DropdownButton<AnalyticsDevice?>(
|
return DropdownButton<AnalyticsDevice?>(
|
||||||
value: state.selectedDevice,
|
value: state.selectedDevice,
|
||||||
isDense: true,
|
isDense: true,
|
||||||
@ -60,10 +61,30 @@ class AnalyticsDeviceDropdown extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
style: _getTextStyle(context),
|
style: _getTextStyle(context),
|
||||||
padding: _defaultPadding,
|
padding: _defaultPadding,
|
||||||
|
selectedItemBuilder: (context) {
|
||||||
|
return state.devices.map((e) => Text(e.name)).toList();
|
||||||
|
},
|
||||||
items: state.devices.map((e) {
|
items: state.devices.map((e) {
|
||||||
return DropdownMenuItem(
|
return DropdownMenuItem(
|
||||||
value: e,
|
value: e,
|
||||||
child: Text(e.name),
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(e.name),
|
||||||
|
if (spaceUuid != null)
|
||||||
|
FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
alignment: AlignmentDirectional.centerStart,
|
||||||
|
child: Text(
|
||||||
|
spaceUuid,
|
||||||
|
style: _getTextStyle(context)?.copyWith(
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/helpers/get_month_name_from_int.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
|
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
@ -18,7 +18,10 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BarChart(
|
return BarChart(
|
||||||
BarChartData(
|
BarChartData(
|
||||||
gridData: EnergyManagementChartsHelper.gridData(),
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
|
checkToShowHorizontalLine: (value) => true,
|
||||||
|
horizontalInterval: 250,
|
||||||
|
),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
barTouchData: _barTouchData(context),
|
barTouchData: _barTouchData(context),
|
||||||
titlesData: _titlesData(context),
|
titlesData: _titlesData(context),
|
||||||
@ -31,25 +34,29 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
barRods: [
|
barRods: [
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.1),
|
color: ColorsManager.vividBlue.withValues(alpha: 0.1),
|
||||||
toY: data.phaseA + data.phaseB + data.phaseC,
|
toY: data.energyConsumedA +
|
||||||
|
data.energyConsumedB +
|
||||||
|
data.energyConsumedC,
|
||||||
rodStackItems: [
|
rodStackItems: [
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
0,
|
0,
|
||||||
data.phaseA,
|
data.energyConsumedA,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.8),
|
ColorsManager.vividBlue.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
data.phaseA,
|
data.energyConsumedA,
|
||||||
data.phaseA + data.phaseB,
|
data.energyConsumedA + data.energyConsumedB,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.4),
|
ColorsManager.vividBlue.withValues(alpha: 0.4),
|
||||||
),
|
),
|
||||||
BarChartRodStackItem(
|
BarChartRodStackItem(
|
||||||
data.phaseA + data.phaseB,
|
data.energyConsumedA + data.energyConsumedB,
|
||||||
data.phaseA + data.phaseB + data.phaseC,
|
data.energyConsumedA +
|
||||||
|
data.energyConsumedB +
|
||||||
|
data.energyConsumedC,
|
||||||
ColorsManager.vividBlue.withValues(alpha: 0.15),
|
ColorsManager.vividBlue.withValues(alpha: 0.15),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
width: 16,
|
width: 8,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(8),
|
topLeft: Radius.circular(8),
|
||||||
topRight: Radius.circular(8),
|
topRight: Radius.circular(8),
|
||||||
@ -59,6 +66,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
duration: Duration.zero,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,18 +99,27 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
final data = energyData;
|
final data = energyData;
|
||||||
|
|
||||||
final month = data[group.x.toInt()].month.getMonthName;
|
final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date);
|
||||||
final phaseA = data[group.x.toInt()].phaseA;
|
final phaseA = data[group.x.toInt()].energyConsumedA;
|
||||||
final phaseB = data[group.x.toInt()].phaseB;
|
final phaseB = data[group.x.toInt()].energyConsumedB;
|
||||||
final phaseC = data[group.x.toInt()].phaseC;
|
final phaseC = data[group.x.toInt()].energyConsumedC;
|
||||||
|
final total = data[group.x.toInt()].energyConsumedKw;
|
||||||
|
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
'$month\n',
|
'$date\n',
|
||||||
context.textTheme.bodyMedium!.copyWith(
|
context.textTheme.bodyMedium!.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.start,
|
||||||
children: [
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: 'Total: $total\n',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: 'Phase A: $phaseA\n',
|
text: 'Phase A: $phaseA\n',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
@ -144,9 +161,9 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
sideTitles: SideTitles(
|
sideTitles: SideTitles(
|
||||||
showTitles: true,
|
showTitles: true,
|
||||||
getTitlesWidget: (value, _) {
|
getTitlesWidget: (value, _) {
|
||||||
final month = energyData[value.toInt()].month.getMonthName;
|
final month = DateFormat('d').format(energyData[value.toInt()].date);
|
||||||
return FittedBox(
|
return FittedBox(
|
||||||
alignment: AlignmentDirectional.bottomCenter,
|
alignment: AlignmentDirectional.center,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: RotatedBox(
|
child: RotatedBox(
|
||||||
quarterTurns: 3,
|
quarterTurns: 3,
|
||||||
@ -160,7 +177,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
reservedSize: 36,
|
reservedSize: 18,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -16,7 +16,11 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
leftTitlesInterval: 250,
|
leftTitlesInterval: 250,
|
||||||
),
|
),
|
||||||
gridData: EnergyManagementChartsHelper.gridData(),
|
|
||||||
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
|
checkToShowHorizontalLine: (value) => true,
|
||||||
|
horizontalInterval: 250,
|
||||||
|
),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||||
lineBarsData: chartData.map((e) {
|
lineBarsData: chartData.map((e) {
|
||||||
@ -33,7 +37,7 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
duration: Durations.extralong1,
|
duration: Duration.zero,
|
||||||
curve: Curves.easeIn,
|
curve: Curves.easeIn,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_per_device/energy_consumption_per_device_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart';
|
||||||
@ -46,6 +47,7 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
|||||||
flex: 2,
|
flex: 2,
|
||||||
child: EnergyConsumptionPerDeviceDevicesList(
|
child: EnergyConsumptionPerDeviceDevicesList(
|
||||||
chartData: state.chartData,
|
chartData: state.chartData,
|
||||||
|
devices: context.watch<AnalyticsDevicesBloc>().state.devices,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/models/analytics_device.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart';
|
import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class EnergyConsumptionPerDeviceDevicesList extends StatelessWidget {
|
class EnergyConsumptionPerDeviceDevicesList extends StatelessWidget {
|
||||||
const EnergyConsumptionPerDeviceDevicesList({required this.chartData, super.key});
|
const EnergyConsumptionPerDeviceDevicesList({
|
||||||
|
required this.chartData,
|
||||||
|
required this.devices,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<AnalyticsDevice> devices;
|
||||||
final List<DeviceEnergyDataModel> chartData;
|
final List<DeviceEnergyDataModel> chartData;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -16,45 +22,60 @@ class EnergyConsumptionPerDeviceDevicesList extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: chartData.map((e) => _buildDeviceCell(context, e)).toList(),
|
children: devices.map((e) => _buildDeviceCell(context, e)).toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDeviceCell(BuildContext context, DeviceEnergyDataModel device) {
|
Widget _buildDeviceCell(BuildContext context, AnalyticsDevice device) {
|
||||||
return Container(
|
final deviceColor = chartData
|
||||||
height: MediaQuery.sizeOf(context).height * 0.0365,
|
.firstWhere(
|
||||||
padding: const EdgeInsetsDirectional.symmetric(
|
(element) => element.deviceId == device.uuid,
|
||||||
vertical: 8,
|
orElse: () => const DeviceEnergyDataModel(
|
||||||
horizontal: 12,
|
energy: [],
|
||||||
),
|
deviceName: '',
|
||||||
decoration: BoxDecoration(
|
deviceId: '',
|
||||||
borderRadius: BorderRadiusDirectional.circular(8),
|
color: Colors.red,
|
||||||
border: Border.all(
|
),
|
||||||
color: ColorsManager.greyColor,
|
)
|
||||||
width: 1,
|
.color;
|
||||||
|
|
||||||
|
return Tooltip(
|
||||||
|
message: '${device.name}\n${device.productDevice?.uuid ?? ''}',
|
||||||
|
child: Container(
|
||||||
|
height: MediaQuery.sizeOf(context).height * 0.0365,
|
||||||
|
padding: const EdgeInsetsDirectional.symmetric(
|
||||||
|
vertical: 8,
|
||||||
|
horizontal: 12,
|
||||||
),
|
),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
child: FittedBox(
|
borderRadius: BorderRadiusDirectional.circular(8),
|
||||||
fit: BoxFit.scaleDown,
|
border: Border.all(
|
||||||
alignment: Alignment.center,
|
color: ColorsManager.greyColor,
|
||||||
child: Row(
|
width: 1,
|
||||||
spacing: 6,
|
),
|
||||||
children: [
|
),
|
||||||
CircleAvatar(
|
child: FittedBox(
|
||||||
radius: 4,
|
fit: BoxFit.scaleDown,
|
||||||
backgroundColor: device.color,
|
alignment: Alignment.center,
|
||||||
),
|
child: Row(
|
||||||
Text(
|
spacing: 6,
|
||||||
device.deviceName,
|
children: [
|
||||||
textAlign: TextAlign.center,
|
CircleAvatar(
|
||||||
style: const TextStyle(
|
radius: 4,
|
||||||
color: ColorsManager.blackColor,
|
backgroundColor: deviceColor,
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
),
|
||||||
),
|
Text(
|
||||||
],
|
device.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.dart';
|
import 'package:syncrow_web/pages/analytics/models/power_clamp_energy_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_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/power_clamp_info/power_clamp_info_bloc.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/power_clamp_info/power_clamp_info_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/modules/energy_management/blocs/realtime_device_changes/realtime_device_changes_bloc.dart';
|
||||||
@ -132,6 +133,12 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
alignment: AlignmentDirectional.centerEnd,
|
alignment: AlignmentDirectional.centerEnd,
|
||||||
child: AnalyticsDeviceDropdown(
|
child: AnalyticsDeviceDropdown(
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases(
|
||||||
|
context,
|
||||||
|
powerClampUuid: value.uuid,
|
||||||
|
selectedDate:
|
||||||
|
context.read<AnalyticsDatePickerBloc>().state.monthlyDate,
|
||||||
|
);
|
||||||
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
FetchEnergyManagementDataHelper.loadRealtimeDeviceChanges(
|
||||||
context,
|
context,
|
||||||
deviceUuid: value.uuid,
|
deviceUuid: value.uuid,
|
||||||
|
@ -48,6 +48,9 @@ class PowerClampEnergyStatusWidget extends StatelessWidget {
|
|||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
|
softWrap: true,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
trailing: Text.rich(
|
trailing: Text.rich(
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
@ -4,15 +4,6 @@ import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
|||||||
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
// energy_consumption_chart will return id, name and consumption
|
|
||||||
const phasesJson = {
|
|
||||||
"1": {
|
|
||||||
"phaseOne": 1000,
|
|
||||||
"phaseTwo": 2000,
|
|
||||||
"phaseThree": 3000,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TotalEnergyConsumptionChart extends StatelessWidget {
|
class TotalEnergyConsumptionChart extends StatelessWidget {
|
||||||
const TotalEnergyConsumptionChart({required this.chartData, super.key});
|
const TotalEnergyConsumptionChart({required this.chartData, super.key});
|
||||||
|
|
||||||
@ -23,13 +14,19 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
|||||||
return Expanded(
|
return Expanded(
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
titlesData: EnergyManagementChartsHelper.titlesData(context),
|
titlesData: EnergyManagementChartsHelper.titlesData(
|
||||||
gridData: EnergyManagementChartsHelper.gridData(),
|
context,
|
||||||
|
leftTitlesInterval: 250,
|
||||||
|
),
|
||||||
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
|
checkToShowHorizontalLine: (value) => true,
|
||||||
|
horizontalInterval: 250,
|
||||||
|
),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
lineTouchData: EnergyManagementChartsHelper.lineTouchData(),
|
||||||
lineBarsData: _lineBarsData,
|
lineBarsData: _lineBarsData,
|
||||||
),
|
),
|
||||||
duration: Durations.extralong1,
|
duration: Duration.zero,
|
||||||
curve: Curves.easeIn,
|
curve: Curves.easeIn,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -46,7 +43,7 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
|||||||
.entries
|
.entries
|
||||||
.map(
|
.map(
|
||||||
(entry) => FlSpot(
|
(entry) => FlSpot(
|
||||||
entry.key.toDouble(),
|
entry.value.date.day.toDouble(),
|
||||||
entry.value.value,
|
entry.value.value,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,6 @@ abstract final class FetchOccupancyDataHelper {
|
|||||||
|
|
||||||
loadOccupancyChartData(
|
loadOccupancyChartData(
|
||||||
context,
|
context,
|
||||||
communityUuid: communityId,
|
|
||||||
spaceUuid: spaceId,
|
spaceUuid: spaceId,
|
||||||
date: datePickerState.monthlyDate,
|
date: datePickerState.monthlyDate,
|
||||||
);
|
);
|
||||||
@ -59,16 +58,14 @@ abstract final class FetchOccupancyDataHelper {
|
|||||||
|
|
||||||
static void loadOccupancyChartData(
|
static void loadOccupancyChartData(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
required String communityUuid,
|
|
||||||
required String spaceUuid,
|
required String spaceUuid,
|
||||||
required DateTime date,
|
required DateTime date,
|
||||||
}) {
|
}) {
|
||||||
context.read<OccupancyBloc>().add(
|
context.read<OccupancyBloc>().add(
|
||||||
LoadOccupancyEvent(
|
LoadOccupancyEvent(
|
||||||
GetOccupancyParam(
|
GetOccupancyParam(
|
||||||
monthDate: '${date.year}-${date.month}',
|
monthDate: '${date.year}-${date.month.toString().padLeft(2, '0')}',
|
||||||
spaceUuid: spaceUuid,
|
spaceUuid: spaceUuid,
|
||||||
communityUuid: communityUuid,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -20,9 +20,9 @@ class AnalyticsOccupancyView extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 32,
|
spacing: 32,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: height * 0.45, child: const OccupancyEndSideBar()),
|
SizedBox(height: height * 0.46, child: const OccupancyEndSideBar()),
|
||||||
SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
|
SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
|
||||||
SizedBox(height: height * 0.5, child: const Placeholder()),
|
SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -16,30 +16,38 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BarChart(
|
return BarChart(
|
||||||
BarChartData(
|
BarChartData(
|
||||||
maxY: 1.0,
|
maxY: 100.0,
|
||||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
checkToShowHorizontalLine: (value) => true,
|
checkToShowHorizontalLine: (value) => true,
|
||||||
horizontalInterval: 0.25,
|
horizontalInterval: 20,
|
||||||
),
|
),
|
||||||
borderData: EnergyManagementChartsHelper.borderData(),
|
borderData: EnergyManagementChartsHelper.borderData(),
|
||||||
barTouchData: _barTouchData(context),
|
barTouchData: _barTouchData(context),
|
||||||
titlesData: _titlesData(context),
|
titlesData: _titlesData(context).copyWith(
|
||||||
|
leftTitles: _titlesData(context).leftTitles.copyWith(
|
||||||
|
sideTitles: _titlesData(context).leftTitles.sideTitles.copyWith(
|
||||||
|
maxIncluded: true,
|
||||||
|
minIncluded: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
barGroups: List.generate(chartData.length, (index) {
|
barGroups: List.generate(chartData.length, (index) {
|
||||||
final actual = chartData[index];
|
final actual = chartData[index];
|
||||||
|
final occupancyValue = double.parse(actual.occupancy);
|
||||||
return BarChartGroupData(
|
return BarChartGroupData(
|
||||||
x: index,
|
x: index,
|
||||||
barsSpace: 0,
|
barsSpace: 0,
|
||||||
groupVertically: true,
|
groupVertically: true,
|
||||||
barRods: [
|
barRods: [
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
toY: 1.0,
|
toY: 100.0,
|
||||||
fromY: double.parse(actual.occupancy) + 0.025,
|
fromY: occupancyValue == 0 ? occupancyValue : occupancyValue + 2.5,
|
||||||
color: ColorsManager.graysColor,
|
color: ColorsManager.graysColor,
|
||||||
width: _chartWidth,
|
width: _chartWidth,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
),
|
),
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
toY: double.parse(actual.occupancy),
|
toY: occupancyValue,
|
||||||
color: ColorsManager.vividBlue.withValues(alpha: 0.8),
|
color: ColorsManager.vividBlue.withValues(alpha: 0.8),
|
||||||
width: _chartWidth,
|
width: _chartWidth,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
@ -81,7 +89,7 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
final data = chartData;
|
final data = chartData;
|
||||||
|
|
||||||
final occupancyValue = double.parse(data[group.x.toInt()].occupancy);
|
final occupancyValue = double.parse(data[group.x.toInt()].occupancy);
|
||||||
final percentage = '${(occupancyValue * 100).toStringAsFixed(0)}%';
|
final percentage = '${(occupancyValue).toStringAsFixed(0)}%';
|
||||||
|
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
percentage,
|
percentage,
|
||||||
@ -101,14 +109,14 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
final leftTitles = titlesData.leftTitles.copyWith(
|
final leftTitles = titlesData.leftTitles.copyWith(
|
||||||
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
|
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
|
||||||
reservedSize: 70,
|
reservedSize: 70,
|
||||||
interval: 0.25,
|
interval: 20,
|
||||||
getTitlesWidget: (value, meta) => Padding(
|
getTitlesWidget: (value, meta) => Padding(
|
||||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(
|
child: Text(
|
||||||
'${(value * 100).toStringAsFixed(0)}%',
|
'${(value).toStringAsFixed(0)}%',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.greyColor,
|
||||||
|
@ -50,9 +50,6 @@ class OccupancyChartBox extends StatelessWidget {
|
|||||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||||
FetchOccupancyDataHelper.loadOccupancyChartData(
|
FetchOccupancyDataHelper.loadOccupancyChartData(
|
||||||
context,
|
context,
|
||||||
communityUuid:
|
|
||||||
spaceTreeState.selectedCommunities.firstOrNull ??
|
|
||||||
'',
|
|
||||||
spaceUuid:
|
spaceUuid:
|
||||||
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
date: value,
|
date: value,
|
||||||
|
@ -1,24 +1,20 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
class GetEnergyConsumptionByPhasesParam extends Equatable {
|
class GetEnergyConsumptionByPhasesParam extends Equatable {
|
||||||
final DateTime? startDate;
|
final String powerClampUuid;
|
||||||
final DateTime? endDate;
|
final DateTime? date;
|
||||||
final String? spaceId;
|
|
||||||
|
|
||||||
const GetEnergyConsumptionByPhasesParam({
|
const GetEnergyConsumptionByPhasesParam({
|
||||||
this.startDate,
|
required this.powerClampUuid,
|
||||||
this.endDate,
|
this.date,
|
||||||
this.spaceId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'startDate': startDate?.toIso8601String(),
|
'monthDate': '${date?.year}-${date?.month.toString().padLeft(2, '0')}',
|
||||||
'endDate': endDate?.toIso8601String(),
|
|
||||||
'spaceId': spaceId,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [startDate, endDate, spaceId];
|
List<Object?> get props => [powerClampUuid, date];
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,11 @@
|
|||||||
class GetOccupancyParam {
|
class GetOccupancyParam {
|
||||||
final String monthDate;
|
final String monthDate;
|
||||||
final String? spaceUuid;
|
final String? spaceUuid;
|
||||||
final String communityUuid;
|
|
||||||
|
|
||||||
GetOccupancyParam({
|
GetOccupancyParam({
|
||||||
required this.monthDate,
|
required this.monthDate,
|
||||||
required this.spaceUuid,
|
required this.spaceUuid,
|
||||||
required this.communityUuid,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() => {'monthDate': monthDate};
|
||||||
return {
|
|
||||||
'monthDate': monthDate,
|
|
||||||
'spaceUuid': spaceUuid,
|
|
||||||
'communityUuid': communityUuid,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/phases_energy_consumption.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_by_phases_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_by_phases/energy_consumption_by_phases_service.dart';
|
|
||||||
|
|
||||||
class FakeEnergyConsumptionByPhasesService
|
|
||||||
implements EnergyConsumptionByPhasesService {
|
|
||||||
@override
|
|
||||||
Future<List<PhasesEnergyConsumption>> load(
|
|
||||||
GetEnergyConsumptionByPhasesParam param,
|
|
||||||
) {
|
|
||||||
return Future.delayed(
|
|
||||||
const Duration(milliseconds: 500),
|
|
||||||
() => const [
|
|
||||||
PhasesEnergyConsumption(month: 1, phaseA: 200, phaseB: 300, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 2, phaseA: 300, phaseB: 400, phaseC: 500),
|
|
||||||
PhasesEnergyConsumption(month: 3, phaseA: 400, phaseB: 500, phaseC: 600),
|
|
||||||
PhasesEnergyConsumption(month: 4, phaseA: 100, phaseB: 100, phaseC: 100),
|
|
||||||
PhasesEnergyConsumption(month: 5, phaseA: 300, phaseB: 400, phaseC: 500),
|
|
||||||
PhasesEnergyConsumption(month: 6, phaseA: 300, phaseB: 100, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 7, phaseA: 300, phaseB: 100, phaseC: 400),
|
|
||||||
PhasesEnergyConsumption(month: 8, phaseA: 500, phaseB: 100, phaseC: 100),
|
|
||||||
PhasesEnergyConsumption(month: 9, phaseA: 500, phaseB: 100, phaseC: 200),
|
|
||||||
PhasesEnergyConsumption(month: 10, phaseA: 100, phaseB: 50, phaseC: 50),
|
|
||||||
PhasesEnergyConsumption(month: 11, phaseA: 600, phaseB: 750, phaseC: 130),
|
|
||||||
PhasesEnergyConsumption(month: 12, phaseA: 100, phaseB: 100, phaseC: 100),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,8 +15,9 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: 'endpoint',
|
path: '/power-clamp/${param.powerClampUuid}/historical',
|
||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
|
queryParameters: param.toJson(),
|
||||||
expectedResponseModel: (data) {
|
expectedResponseModel: (data) {
|
||||||
final json = data as Map<String, dynamic>? ?? {};
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
final mappedData = json['data'] as List<dynamic>? ?? [];
|
final mappedData = json['data'] as List<dynamic>? ?? [];
|
||||||
@ -28,7 +29,7 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to load energy consumption per device: $e');
|
throw Exception('Failed to load energy consumption per phase: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
import 'dart:math' as math show Random;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/device_energy_data_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/models/energy_data_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_energy_consumption_per_device_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/energy_consumption_per_device/energy_consumption_per_device_service.dart';
|
|
||||||
|
|
||||||
class FakeEnergyConsumptionPerDeviceService
|
|
||||||
implements EnergyConsumptionPerDeviceService {
|
|
||||||
@override
|
|
||||||
Future<List<DeviceEnergyDataModel>> load(
|
|
||||||
GetEnergyConsumptionPerDeviceParam param,
|
|
||||||
) {
|
|
||||||
final random = math.Random();
|
|
||||||
return Future.delayed(const Duration(milliseconds: 500), () {
|
|
||||||
return [
|
|
||||||
(Colors.redAccent, 1),
|
|
||||||
(Colors.lightBlueAccent, 2),
|
|
||||||
(Colors.purpleAccent, 3),
|
|
||||||
].map((e) {
|
|
||||||
final (color, index) = e;
|
|
||||||
return DeviceEnergyDataModel(
|
|
||||||
color: color,
|
|
||||||
energy: List.generate(30, (i) => i)
|
|
||||||
.map(
|
|
||||||
(index) => EnergyDataModel(
|
|
||||||
date: DateTime(2025, 1, index + 1),
|
|
||||||
value: random.nextInt(100) + (index * 100),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
deviceName: 'Device $index',
|
|
||||||
deviceId: 'device_$index',
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,10 +34,7 @@ abstract final class _EnergyConsumptionPerDeviceMapper {
|
|||||||
static List<DeviceEnergyDataModel> map(dynamic data) {
|
static List<DeviceEnergyDataModel> map(dynamic data) {
|
||||||
final json = data as Map<String, dynamic>? ?? {};
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
final mappedData = json['data'] as List<dynamic>? ?? [];
|
final mappedData = json['data'] as List<dynamic>? ?? [];
|
||||||
return mappedData.where((e) {
|
return mappedData.map((e) {
|
||||||
final deviceData = (e as Map<String, dynamic>)['data'] as List<dynamic>? ?? [];
|
|
||||||
return deviceData.isNotEmpty;
|
|
||||||
}).map((e) {
|
|
||||||
final deviceData = e as Map<String, dynamic>;
|
final deviceData = e as Map<String, dynamic>;
|
||||||
final energyData = deviceData['data'] as List<dynamic>;
|
final energyData = deviceData['data'] as List<dynamic>;
|
||||||
|
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
|
||||||
import 'package:syncrow_web/pages/analytics/services/occupacy/occupacy_service.dart';
|
|
||||||
|
|
||||||
class FakeOccupacyService implements OccupacyService {
|
|
||||||
@override
|
|
||||||
Future<List<Occupacy>> load(GetOccupancyParam param) async {
|
|
||||||
return await Future.delayed(
|
|
||||||
const Duration(seconds: 1),
|
|
||||||
() => List.generate(
|
|
||||||
30,
|
|
||||||
(index) => Occupacy(
|
|
||||||
date: DateTime.now().subtract(Duration(days: index)).toString(),
|
|
||||||
occupancy: ((index / 100)).toString(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:syncrow_web/pages/analytics/models/occupacy.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/params/get_occupancy_param.dart';
|
||||||
|
import 'package:syncrow_web/pages/analytics/services/occupacy/occupacy_service.dart';
|
||||||
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
|
final class RemoteOccupancyService implements OccupacyService {
|
||||||
|
const RemoteOccupancyService(this._httpService);
|
||||||
|
|
||||||
|
final HTTPService _httpService;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Occupacy>> load(GetOccupancyParam param) async {
|
||||||
|
try {
|
||||||
|
final response = await _httpService.get(
|
||||||
|
path: '/occupancy/duration/space/${param.spaceUuid}',
|
||||||
|
showServerMessage: true,
|
||||||
|
queryParameters: param.toJson(),
|
||||||
|
expectedResponseModel: (data) {
|
||||||
|
final json = data as Map<String, dynamic>? ?? {};
|
||||||
|
final mappedData = json['data'] as List<dynamic>? ?? [];
|
||||||
|
return mappedData.map((e) {
|
||||||
|
final jsonData = e as Map<String, dynamic>;
|
||||||
|
return Occupacy.fromJson(jsonData);
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Failed to load energy consumption per phase: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -63,7 +63,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _compareListOfLists(List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
bool _compareListOfLists(
|
||||||
|
List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||||
// Check if the old and new lists are the same
|
// Check if the old and new lists are the same
|
||||||
if (oldList.length != newList.length) return false;
|
if (oldList.length != newList.length) return false;
|
||||||
|
|
||||||
@ -111,8 +112,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
trackVisibility: true,
|
trackVisibility: true,
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
controller: _horizontalScrollController,
|
controller: _horizontalScrollController,
|
||||||
thumbVisibility: false,
|
thumbVisibility: true,
|
||||||
trackVisibility: false,
|
trackVisibility: true,
|
||||||
notificationPredicate: (notif) => notif.depth == 1,
|
notificationPredicate: (notif) => notif.depth == 1,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
controller: _verticalScrollController,
|
controller: _verticalScrollController,
|
||||||
@ -132,46 +133,60 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
children: [
|
children: [
|
||||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||||
...List.generate(widget.headers.length, (index) {
|
...List.generate(widget.headers.length, (index) {
|
||||||
return _buildTableHeaderCell(widget.headers[index], index);
|
return _buildTableHeaderCell(
|
||||||
|
widget.headers[index], index);
|
||||||
})
|
})
|
||||||
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
widget.isEmpty
|
widget.isEmpty
|
||||||
? Column(
|
? SizedBox(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
height: widget.size.height * 0.5,
|
||||||
children: [
|
width: widget.size.width,
|
||||||
Row(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Column(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
SvgPicture.asset(Assets.emptyTable),
|
children: [
|
||||||
const SizedBox(
|
Column(
|
||||||
height: 15,
|
children: [
|
||||||
),
|
SvgPicture.asset(Assets.emptyTable),
|
||||||
Text(
|
const SizedBox(
|
||||||
widget.tableName == 'AccessManagement' ? 'No Password ' : 'No Devices',
|
height: 15,
|
||||||
style: Theme.of(context)
|
),
|
||||||
.textTheme
|
Text(
|
||||||
.bodySmall!
|
widget.tableName == 'AccessManagement'
|
||||||
.copyWith(color: ColorsManager.grayColor),
|
? 'No Password '
|
||||||
)
|
: 'No Devices',
|
||||||
],
|
style: Theme.of(context)
|
||||||
),
|
.textTheme
|
||||||
],
|
.bodySmall!
|
||||||
),
|
.copyWith(
|
||||||
],
|
color:
|
||||||
|
ColorsManager.grayColor),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: Column(
|
: Column(
|
||||||
children: List.generate(widget.data.length, (index) {
|
children:
|
||||||
|
List.generate(widget.data.length, (index) {
|
||||||
final row = widget.data[index];
|
final row = widget.data[index];
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
if (widget.withCheckBox) _buildRowCheckbox(index, widget.size.height * 0.08),
|
if (widget.withCheckBox)
|
||||||
...row.map((cell) => _buildTableCell(cell.toString(), widget.size.height * 0.08)),
|
_buildRowCheckbox(
|
||||||
|
index, widget.size.height * 0.08),
|
||||||
|
...row.map((cell) => _buildTableCell(
|
||||||
|
cell.toString(),
|
||||||
|
widget.size.height * 0.08)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -196,7 +211,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
),
|
),
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
value: _selectAll,
|
value: _selectAll,
|
||||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
|
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||||
|
? _toggleSelectAll
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -238,7 +255,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
constraints: const BoxConstraints.expand(height: 40),
|
constraints: const BoxConstraints.expand(height: 40),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: index == widget.headers.length - 1 ? 12 : 8.0, vertical: 4),
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
||||||
|
vertical: 4),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: context.textTheme.titleSmall!.copyWith(
|
style: context.textTheme.titleSmall!.copyWith(
|
||||||
|
@ -97,7 +97,8 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Space Name:',
|
_buildInfoRow('Space Name:',
|
||||||
device.spaces?.firstOrNull?.spaceName ?? 'N/A'),
|
device.spaces?.firstOrNull?.spaceName ?? 'N/A'),
|
||||||
_buildInfoRow('Room:', device.subspace?.subspaceName ?? 'N/A'),
|
_buildInfoRow(
|
||||||
|
'Sub space:', device.subspace?.subspaceName ?? 'N/A'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
|
@ -51,12 +51,13 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
|
|
||||||
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
||||||
try {
|
try {
|
||||||
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
var uuid =
|
||||||
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
user = await HomeApi().fetchUserInfo(uuid);
|
user = await HomeApi().fetchUserInfo(uuid);
|
||||||
|
|
||||||
if (user != null && user!.project != null) {
|
if (user != null && user!.project != null) {
|
||||||
await ProjectManager.setProjectUUID(user!.project!.uuid);
|
await ProjectManager.setProjectUUID(user!.project!.uuid);
|
||||||
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
|
// NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
|
||||||
}
|
}
|
||||||
add(FetchTermEvent());
|
add(FetchTermEvent());
|
||||||
add(FetchPolicyEvent());
|
add(FetchPolicyEvent());
|
||||||
@ -88,10 +89,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
Future _confirmUserAgreement(
|
||||||
|
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(LoadingHome());
|
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);
|
policy = await HomeApi().confirmUserAgreements(uuid);
|
||||||
emit(PolicyAgreement());
|
emit(PolicyAgreement());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -155,7 +158,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
},
|
},
|
||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager.primaryColor,
|
||||||
),
|
),
|
||||||
|
|
||||||
// HomeItemModel(
|
// HomeItemModel(
|
||||||
// title: 'Move in',
|
// title: 'Move in',
|
||||||
// icon: Assets.moveinIcon,
|
// icon: Assets.moveinIcon,
|
||||||
|
@ -24,7 +24,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||||
homeBloc.add(const FetchUserInfo());
|
// homeBloc.add(const FetchUserInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -38,8 +38,10 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
child: BlocConsumer<HomeBloc, HomeState>(
|
child: BlocConsumer<HomeBloc, HomeState>(
|
||||||
listener: (BuildContext context, state) {
|
listener: (BuildContext context, state) {
|
||||||
if (state is HomeInitial) {
|
if (state is HomeInitial) {
|
||||||
if (homeBloc.user!.hasAcceptedWebAgreement == false && !_dialogShown) {
|
if (homeBloc.user!.hasAcceptedWebAgreement == false &&
|
||||||
_dialogShown = true; // Set the flag to true to indicate the dialog is showing.
|
!_dialogShown) {
|
||||||
|
_dialogShown =
|
||||||
|
true; // Set the flag to true to indicate the dialog is showing.
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -54,7 +56,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
_dialogShown = false;
|
_dialogShown = false;
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
homeBloc.add(ConfirmUserAgreementEvent());
|
homeBloc.add(ConfirmUserAgreementEvent());
|
||||||
homeBloc.add(const FetchUserInfo());
|
// homeBloc.add(const FetchUserInfo());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -98,7 +100,8 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: homeBloc.homeItems.length,
|
itemCount: homeBloc.homeItems.length,
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3, // Adjust as needed.
|
crossAxisCount: 3, // Adjust as needed.
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
mainAxisSpacing: 20.0,
|
mainAxisSpacing: 20.0,
|
||||||
@ -110,7 +113,8 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
active: homeBloc.homeItems[index].active!,
|
active: homeBloc.homeItems[index].active!,
|
||||||
name: homeBloc.homeItems[index].title!,
|
name: homeBloc.homeItems[index].title!,
|
||||||
img: homeBloc.homeItems[index].icon!,
|
img: homeBloc.homeItems[index].icon!,
|
||||||
onTap: () => homeBloc.homeItems[index].onPress(context),
|
onTap: () =>
|
||||||
|
homeBloc.homeItems[index].onPress(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -26,8 +26,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
|||||||
functionCode: event.functionData.functionCode,
|
functionCode: event.functionData.functionCode,
|
||||||
operationName: event.functionData.operationName,
|
operationName: event.functionData.operationName,
|
||||||
value: event.functionData.value ?? existingData.value,
|
value: event.functionData.value ?? existingData.value,
|
||||||
valueDescription: event.functionData.valueDescription ?? existingData.valueDescription,
|
valueDescription: event.functionData.valueDescription ??
|
||||||
|
existingData.valueDescription,
|
||||||
condition: event.functionData.condition ?? existingData.condition,
|
condition: event.functionData.condition ?? existingData.condition,
|
||||||
|
step: event.functionData.step ?? existingData.step,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
functions.clear();
|
functions.clear();
|
||||||
@ -59,8 +61,10 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onSelectFunction(SelectFunction event, Emitter<FunctionBlocState> emit) {
|
FutureOr<void> _onSelectFunction(
|
||||||
|
SelectFunction event, Emitter<FunctionBlocState> emit) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(
|
||||||
selectedFunction: event.functionCode, selectedOperationName: event.operationName));
|
selectedFunction: event.functionCode,
|
||||||
|
selectedOperationName: event.operationName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@ abstract class ACFunction extends DeviceFunction<AcStatusModel> {
|
|||||||
required super.operationName,
|
required super.operationName,
|
||||||
required super.icon,
|
required super.icon,
|
||||||
required this.type,
|
required this.type,
|
||||||
|
super.step,
|
||||||
|
super.unit,
|
||||||
|
super.max,
|
||||||
|
super.min,
|
||||||
});
|
});
|
||||||
|
|
||||||
List<ACOperationalValue> getOperationalValues();
|
List<ACOperationalValue> getOperationalValues();
|
||||||
@ -75,26 +79,24 @@ class ModeFunction extends ACFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TempSetFunction extends ACFunction {
|
class TempSetFunction extends ACFunction {
|
||||||
final int min;
|
TempSetFunction({
|
||||||
final int max;
|
required super.deviceId,
|
||||||
final int step;
|
required super.deviceName,
|
||||||
|
required super.type,
|
||||||
TempSetFunction(
|
}) : super(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
|
||||||
: min = 160,
|
|
||||||
max = 300,
|
|
||||||
step = 1,
|
|
||||||
super(
|
|
||||||
code: 'temp_set',
|
code: 'temp_set',
|
||||||
operationName: 'Set Temperature',
|
operationName: 'Set Temperature',
|
||||||
icon: Assets.assetsTempreture,
|
icon: Assets.assetsTempreture,
|
||||||
type: type,
|
min: 200,
|
||||||
|
max: 300,
|
||||||
|
step: 1,
|
||||||
|
unit: "°C",
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<ACOperationalValue> getOperationalValues() {
|
List<ACOperationalValue> getOperationalValues() {
|
||||||
List<ACOperationalValue> values = [];
|
List<ACOperationalValue> values = [];
|
||||||
for (int temp = min; temp <= max; temp += step) {
|
for (int temp = min!.toInt(); temp <= max!; temp += step!.toInt()) {
|
||||||
values.add(ACOperationalValue(
|
values.add(ACOperationalValue(
|
||||||
icon: Assets.assetsTempreture,
|
icon: Assets.assetsTempreture,
|
||||||
description: "${temp / 10}°C",
|
description: "${temp / 10}°C",
|
||||||
@ -104,7 +106,6 @@ class TempSetFunction extends ACFunction {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LevelFunction extends ACFunction {
|
class LevelFunction extends ACFunction {
|
||||||
LevelFunction(
|
LevelFunction(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
{required super.deviceId, required super.deviceName, required type})
|
||||||
@ -166,9 +167,10 @@ class ChildLockFunction extends ACFunction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CurrentTempFunction extends ACFunction {
|
class CurrentTempFunction extends ACFunction {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
final String unit = "°C";
|
||||||
|
|
||||||
CurrentTempFunction(
|
CurrentTempFunction(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
{required super.deviceId, required super.deviceName, required type})
|
||||||
@ -185,7 +187,7 @@ class CurrentTempFunction extends ACFunction {
|
|||||||
@override
|
@override
|
||||||
List<ACOperationalValue> getOperationalValues() {
|
List<ACOperationalValue> getOperationalValues() {
|
||||||
List<ACOperationalValue> values = [];
|
List<ACOperationalValue> values = [];
|
||||||
for (int temp = min; temp <= max; temp += step) {
|
for (int temp = min.toInt(); temp <= max; temp += step.toInt()) {
|
||||||
values.add(ACOperationalValue(
|
values.add(ACOperationalValue(
|
||||||
icon: Assets.currentTemp,
|
icon: Assets.currentTemp,
|
||||||
description: "${temp / 10}°C",
|
description: "${temp / 10}°C",
|
||||||
|
@ -6,10 +6,12 @@ class CpsOperationalValue {
|
|||||||
final String description;
|
final String description;
|
||||||
final dynamic value;
|
final dynamic value;
|
||||||
|
|
||||||
|
|
||||||
CpsOperationalValue({
|
CpsOperationalValue({
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.value,
|
required this.value,
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,9 +96,9 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
|||||||
icon: Assets.sensitivity,
|
icon: Assets.sensitivity,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
static const _images = <String>[
|
static const _images = <String>[
|
||||||
Assets.sensitivityFeature1,
|
Assets.sensitivityFeature1,
|
||||||
@ -115,10 +117,10 @@ final class CpsSensitivityFunction extends CpsFunctions {
|
|||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
final values = <CpsOperationalValue>[];
|
final values = <CpsOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min; value <= max; value += step.toInt()) {
|
||||||
values.add(
|
values.add(
|
||||||
CpsOperationalValue(
|
CpsOperationalValue(
|
||||||
icon: _images[value],
|
icon: _images[value.toInt()],
|
||||||
description: '$value',
|
description: '$value',
|
||||||
value: value,
|
value: value,
|
||||||
),
|
),
|
||||||
@ -142,9 +144,9 @@ final class CpsMovingSpeedFunction extends CpsFunctions {
|
|||||||
icon: Assets.speedoMeter,
|
icon: Assets.speedoMeter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
@ -173,9 +175,9 @@ final class CpsSpatialStaticValueFunction extends CpsFunctions {
|
|||||||
icon: Assets.spatialStaticValue,
|
icon: Assets.spatialStaticValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
@ -204,9 +206,9 @@ final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
|||||||
icon: Assets.spatialMotionValue,
|
icon: Assets.spatialMotionValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
@ -375,9 +377,9 @@ final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
|||||||
icon: Assets.presenceJudgementThrshold,
|
icon: Assets.presenceJudgementThrshold,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
@ -406,9 +408,9 @@ final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
|||||||
icon: Assets.presenceJudgementThrshold,
|
icon: Assets.presenceJudgementThrshold,
|
||||||
);
|
);
|
||||||
|
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<CpsOperationalValue> getOperationalValues() {
|
List<CpsOperationalValue> getOperationalValues() {
|
||||||
|
@ -4,6 +4,11 @@ abstract class DeviceFunction<T> {
|
|||||||
final String code;
|
final String code;
|
||||||
final String operationName;
|
final String operationName;
|
||||||
final String icon;
|
final String icon;
|
||||||
|
final double? step;
|
||||||
|
final String? unit;
|
||||||
|
final double? max;
|
||||||
|
final double? min;
|
||||||
|
|
||||||
|
|
||||||
DeviceFunction({
|
DeviceFunction({
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
@ -11,6 +16,10 @@ abstract class DeviceFunction<T> {
|
|||||||
required this.code,
|
required this.code,
|
||||||
required this.operationName,
|
required this.operationName,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
|
this.step,
|
||||||
|
this.unit,
|
||||||
|
this.max,
|
||||||
|
this.min,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +31,10 @@ class DeviceFunctionData {
|
|||||||
final dynamic value;
|
final dynamic value;
|
||||||
final String? condition;
|
final String? condition;
|
||||||
final String? valueDescription;
|
final String? valueDescription;
|
||||||
|
final double? step;
|
||||||
|
final String? unit;
|
||||||
|
final double? max;
|
||||||
|
final double? min;
|
||||||
|
|
||||||
DeviceFunctionData({
|
DeviceFunctionData({
|
||||||
required this.entityId,
|
required this.entityId,
|
||||||
@ -31,6 +44,10 @@ class DeviceFunctionData {
|
|||||||
required this.value,
|
required this.value,
|
||||||
this.condition,
|
this.condition,
|
||||||
this.valueDescription,
|
this.valueDescription,
|
||||||
|
this.step,
|
||||||
|
this.unit,
|
||||||
|
this.max,
|
||||||
|
this.min,
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@ -42,6 +59,10 @@ class DeviceFunctionData {
|
|||||||
'value': value,
|
'value': value,
|
||||||
if (condition != null) 'condition': condition,
|
if (condition != null) 'condition': condition,
|
||||||
if (valueDescription != null) 'valueDescription': valueDescription,
|
if (valueDescription != null) 'valueDescription': valueDescription,
|
||||||
|
if (step != null) 'step': step,
|
||||||
|
if (unit != null) 'unit': unit,
|
||||||
|
if (max != null) 'max': max,
|
||||||
|
if (min != null) 'min': min,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,6 +75,10 @@ class DeviceFunctionData {
|
|||||||
value: json['value'],
|
value: json['value'],
|
||||||
condition: json['condition'],
|
condition: json['condition'],
|
||||||
valueDescription: json['valueDescription'],
|
valueDescription: json['valueDescription'],
|
||||||
|
step: json['step']?.toDouble(),
|
||||||
|
unit: json['unit'],
|
||||||
|
max: json['max']?.toDouble(),
|
||||||
|
min: json['min']?.toDouble(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +93,11 @@ class DeviceFunctionData {
|
|||||||
other.operationName == operationName &&
|
other.operationName == operationName &&
|
||||||
other.value == value &&
|
other.value == value &&
|
||||||
other.condition == condition &&
|
other.condition == condition &&
|
||||||
other.valueDescription == valueDescription;
|
other.valueDescription == valueDescription &&
|
||||||
|
other.step == step &&
|
||||||
|
other.unit == unit &&
|
||||||
|
other.max == max &&
|
||||||
|
other.min == min;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -79,6 +108,10 @@ class DeviceFunctionData {
|
|||||||
operationName.hashCode ^
|
operationName.hashCode ^
|
||||||
value.hashCode ^
|
value.hashCode ^
|
||||||
condition.hashCode ^
|
condition.hashCode ^
|
||||||
valueDescription.hashCode;
|
valueDescription.hashCode ^
|
||||||
|
step.hashCode ^
|
||||||
|
unit.hashCode ^
|
||||||
|
max.hashCode ^
|
||||||
|
min.hashCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,11 @@ abstract class FlushFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushPresenceDelayFunction extends FlushFunctions {
|
class FlushPresenceDelayFunction extends FlushFunctions {
|
||||||
final int min;
|
|
||||||
FlushPresenceDelayFunction({
|
FlushPresenceDelayFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : min = 0,
|
}) :
|
||||||
super(
|
super(
|
||||||
code: FlushMountedPresenceSensorModel.codePresenceState,
|
code: FlushMountedPresenceSensorModel.codePresenceState,
|
||||||
operationName: 'Presence State',
|
operationName: 'Presence State',
|
||||||
@ -50,9 +49,9 @@ class FlushPresenceDelayFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushSensiReduceFunction extends FlushFunctions {
|
class FlushSensiReduceFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
FlushSensiReduceFunction({
|
FlushSensiReduceFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -80,8 +79,8 @@ class FlushSensiReduceFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushNoneDelayFunction extends FlushFunctions {
|
class FlushNoneDelayFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushNoneDelayFunction({
|
FlushNoneDelayFunction({
|
||||||
@ -110,9 +109,9 @@ class FlushNoneDelayFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushIlluminanceFunction extends FlushFunctions {
|
class FlushIlluminanceFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
FlushIlluminanceFunction({
|
FlushIlluminanceFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -130,7 +129,7 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
List<FlushOperationalValue> values = [];
|
List<FlushOperationalValue> values = [];
|
||||||
for (int lux = min; lux <= max; lux += step) {
|
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.IlluminanceIcon,
|
icon: Assets.IlluminanceIcon,
|
||||||
description: "$lux Lux",
|
description: "$lux Lux",
|
||||||
@ -142,9 +141,9 @@ class FlushIlluminanceFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushOccurDistReduceFunction extends FlushFunctions {
|
class FlushOccurDistReduceFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
FlushOccurDistReduceFunction({
|
FlushOccurDistReduceFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -173,9 +172,9 @@ class FlushOccurDistReduceFunction extends FlushFunctions {
|
|||||||
|
|
||||||
// ==== then functions ====
|
// ==== then functions ====
|
||||||
class FlushSensitivityFunction extends FlushFunctions {
|
class FlushSensitivityFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
FlushSensitivityFunction({
|
FlushSensitivityFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -203,9 +202,9 @@ class FlushSensitivityFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushNearDetectionFunction extends FlushFunctions {
|
class FlushNearDetectionFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final double max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushNearDetectionFunction({
|
FlushNearDetectionFunction({
|
||||||
@ -225,7 +224,7 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min.toDouble(); value <= max; value += step) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -237,9 +236,9 @@ class FlushNearDetectionFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushMaxDetectDistFunction extends FlushFunctions {
|
class FlushMaxDetectDistFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushMaxDetectDistFunction({
|
FlushMaxDetectDistFunction({
|
||||||
@ -259,7 +258,7 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min; value <= max; value += step.toInt()) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -271,9 +270,9 @@ class FlushMaxDetectDistFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushTargetConfirmTimeFunction({
|
FlushTargetConfirmTimeFunction({
|
||||||
@ -293,7 +292,7 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min.toDouble(); value <= max; value += step) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -305,9 +304,9 @@ class FlushTargetConfirmTimeFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushDisappeDelayFunction extends FlushFunctions {
|
class FlushDisappeDelayFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushDisappeDelayFunction({
|
FlushDisappeDelayFunction({
|
||||||
@ -327,7 +326,7 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min.toDouble(); value <= max; value += step) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -339,9 +338,9 @@ class FlushDisappeDelayFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushIndentLevelFunction extends FlushFunctions {
|
class FlushIndentLevelFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushIndentLevelFunction({
|
FlushIndentLevelFunction({
|
||||||
@ -361,7 +360,7 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min.toDouble(); value <= max; value += step) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -373,9 +372,9 @@ class FlushIndentLevelFunction extends FlushFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FlushTriggerLevelFunction extends FlushFunctions {
|
class FlushTriggerLevelFunction extends FlushFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FlushTriggerLevelFunction({
|
FlushTriggerLevelFunction({
|
||||||
@ -395,7 +394,7 @@ class FlushTriggerLevelFunction extends FlushFunctions {
|
|||||||
@override
|
@override
|
||||||
List<FlushOperationalValue> getOperationalValues() {
|
List<FlushOperationalValue> getOperationalValues() {
|
||||||
final values = <FlushOperationalValue>[];
|
final values = <FlushOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min.toDouble(); value <= max; value += step) {
|
||||||
values.add(FlushOperationalValue(
|
values.add(FlushOperationalValue(
|
||||||
icon: Assets.nobodyTime,
|
icon: Assets.nobodyTime,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
|
@ -20,17 +20,16 @@ abstract class WaterHeaterFunctions
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WHRestartStatusFunction extends WaterHeaterFunctions {
|
class WHRestartStatusFunction extends WaterHeaterFunctions {
|
||||||
final int min;
|
|
||||||
WHRestartStatusFunction({
|
WHRestartStatusFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : min = 0,
|
}) : super(
|
||||||
super(
|
|
||||||
code: 'relay_status',
|
code: 'relay_status',
|
||||||
operationName: 'Restart Status',
|
operationName: 'Restart Status',
|
||||||
icon: Assets.refreshStatusIcon,
|
icon: Assets.refreshStatusIcon,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<WaterHeaterOperationalValue> getOperationalValues() {
|
List<WaterHeaterOperationalValue> getOperationalValues() {
|
||||||
@ -55,13 +54,11 @@ class WHRestartStatusFunction extends WaterHeaterFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class WHSwitchFunction extends WaterHeaterFunctions {
|
class WHSwitchFunction extends WaterHeaterFunctions {
|
||||||
final int min;
|
|
||||||
WHSwitchFunction({
|
WHSwitchFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : min = 0,
|
}) : super(
|
||||||
super(
|
|
||||||
code: 'switch_1',
|
code: 'switch_1',
|
||||||
operationName: 'Switch',
|
operationName: 'Switch',
|
||||||
icon: Assets.assetsAcPower,
|
icon: Assets.assetsAcPower,
|
||||||
@ -104,12 +101,11 @@ class TimerConfirmTimeFunction extends WaterHeaterFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BacklightFunction extends WaterHeaterFunctions {
|
class BacklightFunction extends WaterHeaterFunctions {
|
||||||
final int min;
|
|
||||||
BacklightFunction({
|
BacklightFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
required super.deviceName,
|
required super.deviceName,
|
||||||
required super.type,
|
required super.type,
|
||||||
}) : min = 0,
|
}) :
|
||||||
super(
|
super(
|
||||||
code: 'switch_backlight',
|
code: 'switch_backlight',
|
||||||
operationName: 'Backlight',
|
operationName: 'Backlight',
|
||||||
|
@ -4,7 +4,7 @@ import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
WpsFunctions({
|
WpsFunctions({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -13,6 +13,10 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
|||||||
required super.operationName,
|
required super.operationName,
|
||||||
required super.icon,
|
required super.icon,
|
||||||
required this.type,
|
required this.type,
|
||||||
|
super.step,
|
||||||
|
super.unit,
|
||||||
|
super.max,
|
||||||
|
super.min,
|
||||||
});
|
});
|
||||||
|
|
||||||
List<WpsOperationalValue> getOperationalValues();
|
List<WpsOperationalValue> getOperationalValues();
|
||||||
@ -20,9 +24,13 @@ abstract class WpsFunctions extends DeviceFunction<WallSensorModel> {
|
|||||||
|
|
||||||
// For far_detection (75-600cm in 75cm steps)
|
// For far_detection (75-600cm in 75cm steps)
|
||||||
class FarDetectionFunction extends WpsFunctions {
|
class FarDetectionFunction extends WpsFunctions {
|
||||||
final int min;
|
|
||||||
final int max;
|
final double min;
|
||||||
final int step;
|
@override
|
||||||
|
final double max;
|
||||||
|
@override
|
||||||
|
final double step;
|
||||||
|
@override
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
FarDetectionFunction(
|
FarDetectionFunction(
|
||||||
@ -41,7 +49,7 @@ class FarDetectionFunction extends WpsFunctions {
|
|||||||
@override
|
@override
|
||||||
List<WpsOperationalValue> getOperationalValues() {
|
List<WpsOperationalValue> getOperationalValues() {
|
||||||
final values = <WpsOperationalValue>[];
|
final values = <WpsOperationalValue>[];
|
||||||
for (var value = min; value <= max; value += step) {
|
for (var value = min; value <= max; value += step.toInt()) {
|
||||||
values.add(WpsOperationalValue(
|
values.add(WpsOperationalValue(
|
||||||
icon: Assets.currentDistanceIcon,
|
icon: Assets.currentDistanceIcon,
|
||||||
description: '$value $unit',
|
description: '$value $unit',
|
||||||
@ -54,9 +62,9 @@ class FarDetectionFunction extends WpsFunctions {
|
|||||||
|
|
||||||
// For presence_time (0-65535 minutes)
|
// For presence_time (0-65535 minutes)
|
||||||
class PresenceTimeFunction extends WpsFunctions {
|
class PresenceTimeFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
PresenceTimeFunction(
|
PresenceTimeFunction(
|
||||||
@ -86,9 +94,9 @@ class PresenceTimeFunction extends WpsFunctions {
|
|||||||
|
|
||||||
// For motion_sensitivity_value (1-5 levels)
|
// For motion_sensitivity_value (1-5 levels)
|
||||||
class MotionSensitivityFunction extends WpsFunctions {
|
class MotionSensitivityFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
MotionSensitivityFunction(
|
MotionSensitivityFunction(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
{required super.deviceId, required super.deviceName, required type})
|
||||||
@ -116,9 +124,9 @@ class MotionSensitivityFunction extends WpsFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MotionLessSensitivityFunction extends WpsFunctions {
|
class MotionLessSensitivityFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
MotionLessSensitivityFunction(
|
MotionLessSensitivityFunction(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
{required super.deviceId, required super.deviceName, required type})
|
||||||
@ -171,8 +179,8 @@ class IndicatorFunction extends WpsFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NoOneTimeFunction extends WpsFunctions {
|
class NoOneTimeFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final String unit;
|
final String unit;
|
||||||
|
|
||||||
NoOneTimeFunction(
|
NoOneTimeFunction(
|
||||||
@ -225,9 +233,9 @@ class PresenceStateFunction extends WpsFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class CurrentDistanceFunction extends WpsFunctions {
|
class CurrentDistanceFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
CurrentDistanceFunction(
|
CurrentDistanceFunction(
|
||||||
{required super.deviceId, required super.deviceName, required type})
|
{required super.deviceId, required super.deviceName, required type})
|
||||||
@ -244,11 +252,10 @@ class CurrentDistanceFunction extends WpsFunctions {
|
|||||||
@override
|
@override
|
||||||
List<WpsOperationalValue> getOperationalValues() {
|
List<WpsOperationalValue> getOperationalValues() {
|
||||||
List<WpsOperationalValue> values = [];
|
List<WpsOperationalValue> values = [];
|
||||||
for (int cm = min; cm <= max; cm += step) {
|
for (int cm = min.toInt(); cm <= max; cm += step.toInt()) {
|
||||||
values.add(WpsOperationalValue(
|
values.add(WpsOperationalValue(
|
||||||
icon: Assets.assetsTempreture,
|
icon: Assets.assetsTempreture,
|
||||||
description: "${cm}CM",
|
description: "${cm}CM",
|
||||||
|
|
||||||
value: cm,
|
value: cm,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -257,9 +264,9 @@ class CurrentDistanceFunction extends WpsFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class IlluminanceValueFunction extends WpsFunctions {
|
class IlluminanceValueFunction extends WpsFunctions {
|
||||||
final int min;
|
final double min;
|
||||||
final int max;
|
final double max;
|
||||||
final int step;
|
final double step;
|
||||||
|
|
||||||
IlluminanceValueFunction({
|
IlluminanceValueFunction({
|
||||||
required super.deviceId,
|
required super.deviceId,
|
||||||
@ -277,7 +284,7 @@ class IlluminanceValueFunction extends WpsFunctions {
|
|||||||
@override
|
@override
|
||||||
List<WpsOperationalValue> getOperationalValues() {
|
List<WpsOperationalValue> getOperationalValues() {
|
||||||
List<WpsOperationalValue> values = [];
|
List<WpsOperationalValue> values = [];
|
||||||
for (int lux = min; lux <= max; lux += step) {
|
for (int lux = min.toInt(); lux <= max; lux += step.toInt()) {
|
||||||
values.add(WpsOperationalValue(
|
values.add(WpsOperationalValue(
|
||||||
icon: Assets.IlluminanceIcon,
|
icon: Assets.IlluminanceIcon,
|
||||||
description: "$lux Lux",
|
description: "$lux Lux",
|
||||||
|
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
297
lib/pages/routines/widgets/custom_routines_textbox.dart
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class CustomRoutinesTextbox extends StatefulWidget {
|
||||||
|
final String? currentCondition;
|
||||||
|
final String dialogType;
|
||||||
|
final (double, double) sliderRange;
|
||||||
|
final dynamic displayedValue;
|
||||||
|
final dynamic initialValue;
|
||||||
|
final void Function(String condition) onConditionChanged;
|
||||||
|
final void Function(double value) onTextChanged;
|
||||||
|
final String unit;
|
||||||
|
final double dividendOfRange;
|
||||||
|
final double stepIncreaseAmount;
|
||||||
|
final bool withSpecialChar;
|
||||||
|
|
||||||
|
const CustomRoutinesTextbox({
|
||||||
|
required this.dialogType,
|
||||||
|
required this.sliderRange,
|
||||||
|
required this.displayedValue,
|
||||||
|
required this.initialValue,
|
||||||
|
required this.onConditionChanged,
|
||||||
|
required this.onTextChanged,
|
||||||
|
required this.currentCondition,
|
||||||
|
required this.unit,
|
||||||
|
required this.dividendOfRange,
|
||||||
|
required this.stepIncreaseAmount,
|
||||||
|
required this.withSpecialChar,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CustomRoutinesTextbox> createState() => _CustomRoutinesTextboxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomRoutinesTextboxState extends State<CustomRoutinesTextbox> {
|
||||||
|
late final TextEditingController _controller;
|
||||||
|
bool hasError = false;
|
||||||
|
String? errorMessage;
|
||||||
|
|
||||||
|
int getDecimalPlaces(double step) {
|
||||||
|
String stepStr = step.toString();
|
||||||
|
if (stepStr.contains('.')) {
|
||||||
|
List<String> parts = stepStr.split('.');
|
||||||
|
String decimalPart = parts[1];
|
||||||
|
decimalPart = decimalPart.replaceAll(RegExp(r'0+$'), '');
|
||||||
|
return decimalPart.isEmpty ? 0 : decimalPart.length;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||||
|
double initialValue;
|
||||||
|
if (widget.initialValue != null &&
|
||||||
|
widget.initialValue is num &&
|
||||||
|
(widget.initialValue as num) == 0) {
|
||||||
|
initialValue = 0.0;
|
||||||
|
} else {
|
||||||
|
initialValue = double.tryParse(widget.displayedValue) ?? 0.0;
|
||||||
|
}
|
||||||
|
_controller = TextEditingController(
|
||||||
|
text: initialValue.toStringAsFixed(decimalPlaces),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _validateInput(String value) {
|
||||||
|
final doubleValue = double.tryParse(value);
|
||||||
|
if (doubleValue == null) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = "Invalid number";
|
||||||
|
hasError = true;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final min = widget.sliderRange.$1;
|
||||||
|
final max = widget.sliderRange.$2;
|
||||||
|
|
||||||
|
if (doubleValue < min) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = "Value must be at least $min";
|
||||||
|
hasError = true;
|
||||||
|
});
|
||||||
|
} else if (doubleValue > max) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = "Value must be at most $max";
|
||||||
|
hasError = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||||
|
int factor = pow(10, decimalPlaces).toInt();
|
||||||
|
int scaledStep = (widget.stepIncreaseAmount * factor).round();
|
||||||
|
int scaledValue = (doubleValue * factor).round();
|
||||||
|
|
||||||
|
if (scaledValue % scaledStep != 0) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = "must be a multiple of ${widget.stepIncreaseAmount}";
|
||||||
|
hasError = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = null;
|
||||||
|
hasError = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(CustomRoutinesTextbox oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.initialValue != oldWidget.initialValue) {
|
||||||
|
if (widget.initialValue != null &&
|
||||||
|
widget.initialValue is num &&
|
||||||
|
(widget.initialValue as num) == 0) {
|
||||||
|
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||||
|
_controller.text = 0.0.toStringAsFixed(decimalPlaces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _correctAndUpdateValue(String value) {
|
||||||
|
final doubleValue = double.tryParse(value) ?? 0.0;
|
||||||
|
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||||
|
double rounded = (doubleValue / widget.stepIncreaseAmount).round() *
|
||||||
|
widget.stepIncreaseAmount;
|
||||||
|
rounded = rounded.clamp(widget.sliderRange.$1, widget.sliderRange.$2);
|
||||||
|
rounded = double.parse(rounded.toStringAsFixed(decimalPlaces));
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
hasError = false;
|
||||||
|
errorMessage = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
_controller.text = rounded.toStringAsFixed(decimalPlaces);
|
||||||
|
_controller.selection = TextSelection.fromPosition(
|
||||||
|
TextPosition(offset: _controller.text.length),
|
||||||
|
);
|
||||||
|
widget.onTextChanged(rounded);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
int decimalPlaces = getDecimalPlaces(widget.stepIncreaseAmount);
|
||||||
|
|
||||||
|
List<TextInputFormatter> formatters = [];
|
||||||
|
if (decimalPlaces == 0) {
|
||||||
|
formatters.add(FilteringTextInputFormatter.digitsOnly);
|
||||||
|
} else {
|
||||||
|
formatters.add(FilteringTextInputFormatter.allow(
|
||||||
|
RegExp(r'^\d*\.?\d{0,' + decimalPlaces.toString() + r'}$'),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
formatters.add(RangeInputFormatter(
|
||||||
|
min: widget.sliderRange.$1,
|
||||||
|
max: widget.sliderRange.$2,
|
||||||
|
));
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
if (widget.dialogType == 'IF')
|
||||||
|
ConditionToggle(
|
||||||
|
currentCondition: widget.currentCondition,
|
||||||
|
onChanged: widget.onConditionChanged,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 35, vertical: 2),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Step: ${widget.stepIncreaseAmount}',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 170,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF8F8F8),
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
border: hasError
|
||||||
|
? Border.all(color: Colors.red, width: 1)
|
||||||
|
: Border.all(
|
||||||
|
color: ColorsManager.lightGrayBorderColor, width: 1),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: ColorsManager.blackColor.withOpacity(0.05),
|
||||||
|
blurRadius: 8,
|
||||||
|
offset: const Offset(0, 4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _controller,
|
||||||
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
inputFormatters: widget.withSpecialChar == true
|
||||||
|
? [FilteringTextInputFormatter.digitsOnly]
|
||||||
|
: null,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
onChanged: _validateInput,
|
||||||
|
onFieldSubmitted: _correctAndUpdateValue,
|
||||||
|
onTapOutside: (_) =>
|
||||||
|
_correctAndUpdateValue(_controller.text),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Text(
|
||||||
|
widget.unit,
|
||||||
|
style: context.textTheme.bodyMedium?.copyWith(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.vividBlue,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (errorMessage != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 2.0),
|
||||||
|
child: Text(
|
||||||
|
errorMessage!,
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: Colors.red,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Min. ${widget.sliderRange.$1.toInt()}${widget.unit}',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Max. ${widget.sliderRange.$2.toInt()}${widget.unit}',
|
||||||
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
@ -76,24 +77,20 @@ class ACHelper {
|
|||||||
context: context,
|
context: context,
|
||||||
acFunctions: acFunctions,
|
acFunctions: acFunctions,
|
||||||
device: device,
|
device: device,
|
||||||
onFunctionSelected: (functionCode, operationName) {
|
onFunctionSelected:
|
||||||
|
(functionCode, operationName) {
|
||||||
RoutineTapFunctionHelper.onTapFunction(
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
context,
|
context,
|
||||||
functionCode: functionCode,
|
functionCode: functionCode,
|
||||||
functionOperationName: operationName,
|
functionOperationName: operationName,
|
||||||
functionValueDescription:
|
functionValueDescription:
|
||||||
selectedFunctionData.valueDescription,
|
selectedFunctionData.valueDescription,
|
||||||
deviceUuid: device?.uuid,
|
deviceUuid: device?.uuid,
|
||||||
codesToAddIntoFunctionsWithDefaultValue: [
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
'temp_set',
|
'temp_set',
|
||||||
'temp_current',
|
'temp_current',
|
||||||
],
|
],
|
||||||
defaultValue: functionCode == 'temp_set'
|
defaultValue: 0);
|
||||||
? 200
|
|
||||||
: functionCode == 'temp_current'
|
|
||||||
? -100
|
|
||||||
: 0,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -206,27 +203,61 @@ class ACHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
bool? removeComparators,
|
bool? removeComparators,
|
||||||
}) {
|
}) {
|
||||||
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
|
final selectedFn =
|
||||||
|
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
|
|
||||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||||
final initialValue = selectedFunctionData?.value ?? initialVal;
|
// Convert stored integer value to display value
|
||||||
return _buildTemperatureSelector(
|
final displayValue =
|
||||||
context: context,
|
(selectedFunctionData?.value ?? selectedFn.min ?? 0) / 10;
|
||||||
initialValue: initialValue,
|
final minValue = selectedFn.min! / 10;
|
||||||
selectCode: selectedFunction,
|
final maxValue = selectedFn.max! / 10;
|
||||||
|
return CustomRoutinesTextbox(
|
||||||
|
withSpecialChar: true,
|
||||||
|
dividendOfRange: maxValue,
|
||||||
currentCondition: selectedFunctionData?.condition,
|
currentCondition: selectedFunctionData?.condition,
|
||||||
device: device,
|
dialogType: selectedFn.type,
|
||||||
operationName: operationName,
|
sliderRange: (minValue, maxValue),
|
||||||
selectedFunctionData: selectedFunctionData,
|
displayedValue: displayValue.toStringAsFixed(1),
|
||||||
removeComparators: removeComparators,
|
initialValue: displayValue.toDouble(),
|
||||||
|
unit: selectedFn.unit!,
|
||||||
|
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectedFunction,
|
||||||
|
operationName: selectedFn.operationName,
|
||||||
|
condition: condition,
|
||||||
|
value: 0,
|
||||||
|
step: selectedFn.step,
|
||||||
|
unit: selectedFn.unit,
|
||||||
|
max: selectedFn.max,
|
||||||
|
min: selectedFn.min,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectedFunction,
|
||||||
|
operationName: selectedFn.operationName,
|
||||||
|
value: (value * 10).round(), // Store as integer
|
||||||
|
condition: selectedFunctionData?.condition,
|
||||||
|
step: selectedFn.step,
|
||||||
|
unit: selectedFn.unit,
|
||||||
|
max: selectedFn.max,
|
||||||
|
min: selectedFn.min,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
stepIncreaseAmount: selectedFn.step! / 10, // Convert step for display
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
|
|
||||||
final values = selectedFn.getOperationalValues();
|
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
context: context,
|
context: context,
|
||||||
values: values,
|
values: selectedFn.getOperationalValues(),
|
||||||
selectedValue: selectedFunctionData?.value,
|
selectedValue: selectedFunctionData?.value,
|
||||||
device: device,
|
device: device,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
@ -235,150 +266,151 @@ class ACHelper {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build temperature selector for AC functions dialog
|
// /// Build temperature selector for AC functions dialog
|
||||||
static Widget _buildTemperatureSelector({
|
// static Widget _buildTemperatureSelector({
|
||||||
required BuildContext context,
|
// required BuildContext context,
|
||||||
required dynamic initialValue,
|
// required dynamic initialValue,
|
||||||
required String? currentCondition,
|
// required String? currentCondition,
|
||||||
required String selectCode,
|
// required String selectCode,
|
||||||
AllDevicesModel? device,
|
// AllDevicesModel? device,
|
||||||
required String operationName,
|
// required String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
// DeviceFunctionData? selectedFunctionData,
|
||||||
bool? removeComparators,
|
// bool? removeComparators,
|
||||||
}) {
|
// }) {
|
||||||
return Column(
|
// return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
// mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
// children: [
|
||||||
if (removeComparators != true)
|
// if (removeComparators != true)
|
||||||
_buildConditionToggle(
|
// _buildConditionToggle(
|
||||||
context,
|
// context,
|
||||||
currentCondition,
|
// currentCondition,
|
||||||
selectCode,
|
// selectCode,
|
||||||
device,
|
// device,
|
||||||
operationName,
|
// operationName,
|
||||||
selectedFunctionData,
|
// selectedFunctionData,
|
||||||
),
|
// ),
|
||||||
const SizedBox(height: 20),
|
// const SizedBox(height: 20),
|
||||||
_buildTemperatureDisplay(
|
// _buildTemperatureDisplay(
|
||||||
context,
|
// context,
|
||||||
initialValue,
|
// initialValue,
|
||||||
device,
|
// device,
|
||||||
operationName,
|
// operationName,
|
||||||
selectedFunctionData,
|
// selectedFunctionData,
|
||||||
selectCode,
|
// selectCode,
|
||||||
),
|
// ),
|
||||||
const SizedBox(height: 20),
|
// const SizedBox(height: 20),
|
||||||
_buildTemperatureSlider(
|
// _buildTemperatureSlider(
|
||||||
context,
|
// context,
|
||||||
initialValue,
|
// initialValue,
|
||||||
device,
|
// device,
|
||||||
operationName,
|
// operationName,
|
||||||
selectedFunctionData,
|
// selectedFunctionData,
|
||||||
selectCode,
|
// selectCode,
|
||||||
),
|
// ),
|
||||||
],
|
// ],
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Build condition toggle for AC functions dialog
|
// /// Build condition toggle for AC functions dialog
|
||||||
static Widget _buildConditionToggle(
|
// static Widget _buildConditionToggle(
|
||||||
BuildContext context,
|
// BuildContext context,
|
||||||
String? currentCondition,
|
// String? currentCondition,
|
||||||
String selectCode,
|
// String selectCode,
|
||||||
AllDevicesModel? device,
|
// AllDevicesModel? device,
|
||||||
String operationName,
|
// String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
// DeviceFunctionData? selectedFunctionData,
|
||||||
|
|
||||||
// Function(String) onConditionChanged,
|
// // Function(String) onConditionChanged,
|
||||||
) {
|
// ) {
|
||||||
final conditions = ["<", "==", ">"];
|
// final conditions = ["<", "==", ">"];
|
||||||
|
|
||||||
return ToggleButtons(
|
// return ToggleButtons(
|
||||||
onPressed: (int index) {
|
// onPressed: (int index) {
|
||||||
context.read<FunctionBloc>().add(
|
// context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
// AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
// functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
// entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
// functionCode: selectCode,
|
||||||
operationName: operationName,
|
// operationName: operationName,
|
||||||
condition: conditions[index],
|
// condition: conditions[index],
|
||||||
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
// value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||||
? 200
|
// ? 200
|
||||||
: -100,
|
// : -100,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
// valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
// borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
// selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||||
selectedColor: Colors.white,
|
// selectedColor: Colors.white,
|
||||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
// fillColor: ColorsManager.primaryColorWithOpacity,
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
// color: ColorsManager.primaryColorWithOpacity,
|
||||||
constraints: const BoxConstraints(
|
// constraints: const BoxConstraints(
|
||||||
minHeight: 40.0,
|
// minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
// minWidth: 40.0,
|
||||||
),
|
// ),
|
||||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
// isSelected:
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
// conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
);
|
// children: conditions.map((c) => Text(c)).toList(),
|
||||||
}
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
/// Build temperature display for AC functions dialog
|
// /// Build temperature display for AC functions dialog
|
||||||
static Widget _buildTemperatureDisplay(
|
// static Widget _buildTemperatureDisplay(
|
||||||
BuildContext context,
|
// BuildContext context,
|
||||||
dynamic initialValue,
|
// dynamic initialValue,
|
||||||
AllDevicesModel? device,
|
// AllDevicesModel? device,
|
||||||
String operationName,
|
// String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
// DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
// String selectCode,
|
||||||
) {
|
// ) {
|
||||||
final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
// final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||||
return Container(
|
// return Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
// padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
// color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(10),
|
// borderRadius: BorderRadius.circular(10),
|
||||||
),
|
// ),
|
||||||
child: Text(
|
// child: Text(
|
||||||
'${(initialValue ?? initialVal) / 10}°C',
|
// '${(initialValue ?? initialVal) / 10}°C',
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
// style: context.textTheme.headlineMedium!.copyWith(
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
// color: ColorsManager.primaryColorWithOpacity,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
static Widget _buildTemperatureSlider(
|
// static Widget _buildTemperatureSlider(
|
||||||
BuildContext context,
|
// BuildContext context,
|
||||||
dynamic initialValue,
|
// dynamic initialValue,
|
||||||
AllDevicesModel? device,
|
// AllDevicesModel? device,
|
||||||
String operationName,
|
// String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
// DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
// String selectCode,
|
||||||
) {
|
// ) {
|
||||||
return Slider(
|
// return Slider(
|
||||||
value: initialValue is int ? initialValue.toDouble() : 200.0,
|
// value: initialValue is int ? initialValue.toDouble() : 200.0,
|
||||||
min: selectCode == 'temp_current' ? -100 : 200,
|
// min: selectCode == 'temp_current' ? -100 : 200,
|
||||||
max: selectCode == 'temp_current' ? 900 : 300,
|
// max: selectCode == 'temp_current' ? 900 : 300,
|
||||||
divisions: 10,
|
// divisions: 10,
|
||||||
label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
// label: '${((initialValue ?? 160) / 10).toInt()}°C',
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
context.read<FunctionBloc>().add(
|
// context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
// AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
// functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
// entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
// functionCode: selectCode,
|
||||||
operationName: operationName,
|
// operationName: operationName,
|
||||||
value: value,
|
// value: value,
|
||||||
condition: selectedFunctionData?.condition,
|
// condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
// valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// );
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
static Widget _buildOperationalValuesList({
|
static Widget _buildOperationalValuesList({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
@ -414,7 +446,9 @@ class ACHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected
|
||||||
|
? Icons.radio_button_checked
|
||||||
|
: Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -430,7 +464,8 @@ class ACHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription:
|
||||||
|
selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -41,7 +41,8 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
|
_cpsFunctions =
|
||||||
|
widget.functions.whereType<CpsFunctions>().where((function) {
|
||||||
if (widget.dialogType == 'THEN') {
|
if (widget.dialogType == 'THEN') {
|
||||||
return function.type == 'THEN' || function.type == 'BOTH';
|
return function.type == 'THEN' || function.type == 'BOTH';
|
||||||
}
|
}
|
||||||
@ -149,6 +150,7 @@ class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
|||||||
device: widget.device,
|
device: widget.device,
|
||||||
)
|
)
|
||||||
: CpsDialogSliderSelector(
|
: CpsDialogSliderSelector(
|
||||||
|
step: selectedCpsFunctions.step!,
|
||||||
operations: operations,
|
operations: operations,
|
||||||
selectedFunction: selectedFunction ?? '',
|
selectedFunction: selectedFunction ?? '',
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData: selectedFunctionData,
|
||||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
required this.device,
|
required this.device,
|
||||||
required this.operationName,
|
required this.operationName,
|
||||||
required this.dialogType,
|
required this.dialogType,
|
||||||
|
required this.step,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -26,13 +28,16 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
final AllDevicesModel? device;
|
final AllDevicesModel? device;
|
||||||
final String operationName;
|
final String operationName;
|
||||||
final String dialogType;
|
final String dialogType;
|
||||||
|
final double step;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliderValueSelector(
|
return CustomRoutinesTextbox(
|
||||||
|
withSpecialChar: false,
|
||||||
currentCondition: selectedFunctionData.condition,
|
currentCondition: selectedFunctionData.condition,
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
sliderRange:
|
||||||
|
CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||||
displayedValue: CpsSliderHelpers.displayText(
|
displayedValue: CpsSliderHelpers.displayText(
|
||||||
value: selectedFunctionData.value,
|
value: selectedFunctionData.value,
|
||||||
functionCode: selectedFunctionData.functionCode,
|
functionCode: selectedFunctionData.functionCode,
|
||||||
@ -50,7 +55,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
@ -64,6 +69,7 @@ class CpsDialogSliderSelector extends StatelessWidget {
|
|||||||
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||||
selectedFunctionData.functionCode,
|
selectedFunctionData.functionCode,
|
||||||
),
|
),
|
||||||
|
stepIncreaseAmount: step,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,30 +34,33 @@ class CpsFunctionsList extends StatelessWidget {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final function = cpsFunctions[index];
|
final function = cpsFunctions[index];
|
||||||
return RoutineDialogFunctionListTile(
|
return RoutineDialogFunctionListTile(
|
||||||
iconPath: function.icon,
|
iconPath: function.icon,
|
||||||
operationName: function.operationName,
|
operationName: function.operationName,
|
||||||
onTap: () => RoutineTapFunctionHelper.onTapFunction(
|
onTap: () {
|
||||||
context,
|
RoutineTapFunctionHelper.onTapFunction(
|
||||||
functionCode: function.code,
|
context,
|
||||||
functionOperationName: function.operationName,
|
step: function.step,
|
||||||
functionValueDescription: selectedFunctionData?.valueDescription,
|
functionCode: function.code,
|
||||||
deviceUuid: device?.uuid,
|
functionOperationName: function.operationName,
|
||||||
codesToAddIntoFunctionsWithDefaultValue: [
|
functionValueDescription:
|
||||||
'static_max_dis',
|
selectedFunctionData?.valueDescription,
|
||||||
'presence_reference',
|
deviceUuid: device?.uuid,
|
||||||
'moving_reference',
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
'perceptual_boundary',
|
'static_max_dis',
|
||||||
'moving_boundary',
|
'presence_reference',
|
||||||
'moving_rigger_time',
|
'moving_reference',
|
||||||
'moving_static_time',
|
'perceptual_boundary',
|
||||||
'none_body_time',
|
'moving_boundary',
|
||||||
'moving_max_dis',
|
'moving_rigger_time',
|
||||||
'moving_range',
|
'moving_static_time',
|
||||||
'presence_range',
|
'none_body_time',
|
||||||
if (dialogType == "IF") 'sensitivity',
|
'moving_max_dis',
|
||||||
],
|
'moving_range',
|
||||||
),
|
'presence_range',
|
||||||
);
|
if (dialogType == "IF") 'sensitivity',
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/flush/flush_operational_value.dart';
|
||||||
|
|
||||||
@ -21,22 +22,20 @@ class FlushOperationalValuesList extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
itemCount: values.length,
|
itemCount: values.length,
|
||||||
itemBuilder: (context, index) =>
|
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||||
_buildValueItem(context, values[index]),
|
);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildValueItem(BuildContext context, FlushOperationalValue value) {
|
Widget _buildValueItem(BuildContext context, FlushOperationalValue value) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||||
Expanded(child: _buildValueDescription(value)),
|
Expanded(child: _buildValueDescription(value)),
|
||||||
_buildValueRadio(context, value),
|
_buildValueRadio(context, value),
|
||||||
],
|
],
|
||||||
@ -44,9 +43,6 @@ class FlushOperationalValuesList extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Widget _buildValueDescription(FlushOperationalValue value) {
|
Widget _buildValueDescription(FlushOperationalValue value) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
@ -60,6 +56,4 @@ class FlushOperationalValuesList extends StatelessWidget {
|
|||||||
groupValue: selectedValue,
|
groupValue: selectedValue,
|
||||||
onChanged: (_) => onSelect(value));
|
onChanged: (_) => onSelect(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/flush/flush_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/flush_presence_sensor/flush_operational_values_list.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||||
|
|
||||||
@ -66,7 +67,8 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
|||||||
if (isDistanceDetection) {
|
if (isDistanceDetection) {
|
||||||
initialValue = initialValue / 100;
|
initialValue = initialValue / 100;
|
||||||
}
|
}
|
||||||
return SliderValueSelector(
|
return CustomRoutinesTextbox(
|
||||||
|
withSpecialChar: true,
|
||||||
currentCondition: functionData.condition,
|
currentCondition: functionData.condition,
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: sliderRange,
|
sliderRange: sliderRange,
|
||||||
@ -83,7 +85,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSliderChanged: (value) {
|
onTextChanged: (value) {
|
||||||
final roundedValue = _roundToStep(value, stepSize);
|
final roundedValue = _roundToStep(value, stepSize);
|
||||||
final finalValue =
|
final finalValue =
|
||||||
isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue;
|
isDistanceDetection ? (roundedValue * 100).toInt() : roundedValue;
|
||||||
@ -102,6 +104,7 @@ class FlushValueSelectorWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
unit: _unit,
|
unit: _unit,
|
||||||
dividendOfRange: stepSize,
|
dividendOfRange: stepSize,
|
||||||
|
stepIncreaseAmount: stepSize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ abstract final class RoutineTapFunctionHelper {
|
|||||||
|
|
||||||
static void onTapFunction(
|
static void onTapFunction(
|
||||||
BuildContext context, {
|
BuildContext context, {
|
||||||
|
double? step,
|
||||||
required String functionCode,
|
required String functionCode,
|
||||||
required String functionOperationName,
|
required String functionOperationName,
|
||||||
required String? functionValueDescription,
|
required String? functionValueDescription,
|
||||||
|
@ -4,11 +4,11 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
@ -87,14 +87,15 @@ class OneGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () =>
|
onTap: () => RoutineTapFunctionHelper
|
||||||
RoutineTapFunctionHelper.onTapFunction(
|
.onTapFunction(
|
||||||
context,
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
functionOperationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
functionValueDescription:
|
functionValueDescription:
|
||||||
selectedFunctionData.valueDescription,
|
selectedFunctionData
|
||||||
|
.valueDescription,
|
||||||
deviceUuid: device?.uuid,
|
deviceUuid: device?.uuid,
|
||||||
codesToAddIntoFunctionsWithDefaultValue: [
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
'countdown_1',
|
'countdown_1',
|
||||||
@ -108,14 +109,16 @@ class OneGangSwitchHelper {
|
|||||||
if (selectedFunction != null)
|
if (selectedFunction != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildValueSelector(
|
child: _buildValueSelector(
|
||||||
context: context,
|
context: context,
|
||||||
selectedFunction: selectedFunction,
|
selectedFunction: selectedFunction,
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData:
|
||||||
acFunctions: oneGangFunctions,
|
selectedFunctionData,
|
||||||
device: device,
|
acFunctions: oneGangFunctions,
|
||||||
operationName: selectedOperationName ?? '',
|
device: device,
|
||||||
removeComparetors: removeComparetors,
|
operationName:
|
||||||
),
|
selectedOperationName ?? '',
|
||||||
|
removeComparetors: removeComparetors,
|
||||||
|
dialogType: dialogType),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -172,6 +175,7 @@ class OneGangSwitchHelper {
|
|||||||
AllDevicesModel? device,
|
AllDevicesModel? device,
|
||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
|
required String dialogType,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1') {
|
if (selectedFunction == 'countdown_1') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 0;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
@ -184,6 +188,7 @@ class OneGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData: selectedFunctionData,
|
||||||
removeComparetors: removeComparetors,
|
removeComparetors: removeComparetors,
|
||||||
|
dialogType: dialogType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final selectedFn = acFunctions.firstWhere(
|
final selectedFn = acFunctions.firstWhere(
|
||||||
@ -216,93 +221,18 @@ class OneGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
|
String? dialogType,
|
||||||
}) {
|
}) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (removeComparetors != true)
|
|
||||||
_buildConditionToggle(
|
|
||||||
context,
|
|
||||||
currentCondition,
|
|
||||||
selectCode,
|
|
||||||
device,
|
|
||||||
operationName,
|
|
||||||
selectedFunctionData,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
|
||||||
selectedFunctionData, selectCode),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||||
selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode, dialogType!),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build condition toggle for AC functions dialog
|
|
||||||
static Widget _buildConditionToggle(
|
|
||||||
BuildContext context,
|
|
||||||
String? currentCondition,
|
|
||||||
String selectCode,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
// Function(String) onConditionChanged,
|
|
||||||
) {
|
|
||||||
final conditions = ["<", "==", ">"];
|
|
||||||
|
|
||||||
return ToggleButtons(
|
|
||||||
onPressed: (int index) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
condition: conditions[index],
|
|
||||||
value: selectedFunctionData?.value ?? 0,
|
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
selectedColor: Colors.white,
|
|
||||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: 40.0,
|
|
||||||
minWidth: 40.0,
|
|
||||||
),
|
|
||||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build temperature display for AC functions dialog
|
|
||||||
static Widget _buildCountDownDisplay(
|
|
||||||
BuildContext context,
|
|
||||||
dynamic initialValue,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
String selectCode) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Widget _buildCountDownSlider(
|
static Widget _buildCountDownSlider(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
dynamic initialValue,
|
dynamic initialValue,
|
||||||
@ -310,38 +240,47 @@ class OneGangSwitchHelper {
|
|||||||
String operationName,
|
String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
|
String dialogType,
|
||||||
) {
|
) {
|
||||||
const twelveHoursInSeconds = 43200.0;
|
return CustomRoutinesTextbox(
|
||||||
final operationalValues = SwitchOperationalValue(
|
withSpecialChar: false,
|
||||||
icon: '',
|
currentCondition: selectedFunctionData?.condition,
|
||||||
description: "sec",
|
dialogType: dialogType,
|
||||||
value: 0.0,
|
sliderRange: (0, 43200),
|
||||||
minValue: 0,
|
displayedValue: (initialValue ?? 0).toString(),
|
||||||
maxValue: twelveHoursInSeconds,
|
initialValue: (initialValue ?? 0).toString(),
|
||||||
stepValue: 1,
|
onConditionChanged: (condition) {
|
||||||
);
|
|
||||||
return Slider(
|
|
||||||
value: (initialValue ?? 0).toDouble(),
|
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
|
||||||
divisions:
|
|
||||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
|
||||||
(operationalValues.stepValue ?? 1))
|
|
||||||
.round(),
|
|
||||||
onChanged: (value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value,
|
condition: condition,
|
||||||
|
value: selectedFunctionData?.value ?? 0,
|
||||||
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTextChanged: (value) {
|
||||||
|
final roundedValue = value.round();
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: roundedValue,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
unit: 'sec',
|
||||||
|
dividendOfRange: 1,
|
||||||
|
stepIncreaseAmount: 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +316,9 @@ class OneGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected
|
||||||
|
? Icons.radio_button_checked
|
||||||
|
: Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -393,7 +334,8 @@ class OneGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription:
|
||||||
|
selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,10 +4,10 @@ import 'package:flutter_svg/flutter_svg.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
@ -86,20 +86,21 @@ class ThreeGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () =>
|
onTap: () => RoutineTapFunctionHelper
|
||||||
RoutineTapFunctionHelper.onTapFunction(
|
.onTapFunction(
|
||||||
context,
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
functionOperationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
functionValueDescription:
|
functionValueDescription:
|
||||||
selectedFunctionData.valueDescription,
|
selectedFunctionData
|
||||||
|
.valueDescription,
|
||||||
deviceUuid: device?.uuid,
|
deviceUuid: device?.uuid,
|
||||||
codesToAddIntoFunctionsWithDefaultValue: [
|
codesToAddIntoFunctionsWithDefaultValue:
|
||||||
'countdown_1',
|
function.code
|
||||||
'countdown_2',
|
.startsWith('countdown')
|
||||||
'countdown_3',
|
? [function.code]
|
||||||
],
|
: [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -109,14 +110,16 @@ class ThreeGangSwitchHelper {
|
|||||||
if (selectedFunction != null)
|
if (selectedFunction != null)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildValueSelector(
|
child: _buildValueSelector(
|
||||||
context: context,
|
context: context,
|
||||||
selectedFunction: selectedFunction,
|
selectedFunction: selectedFunction,
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData:
|
||||||
switchFunctions: switchFunctions,
|
selectedFunctionData,
|
||||||
device: device,
|
switchFunctions: switchFunctions,
|
||||||
operationName: selectedOperationName ?? '',
|
device: device,
|
||||||
removeComparetors: removeComparetors,
|
operationName:
|
||||||
),
|
selectedOperationName ?? '',
|
||||||
|
removeComparetors: removeComparetors,
|
||||||
|
dialogType: dialogType),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -133,14 +136,6 @@ class ThreeGangSwitchHelper {
|
|||||||
onConfirm: state.addedFunctions.isNotEmpty
|
onConfirm: state.addedFunctions.isNotEmpty
|
||||||
? () {
|
? () {
|
||||||
/// add the functions to the routine bloc
|
/// add the functions to the routine bloc
|
||||||
// for (var function in state.addedFunctions) {
|
|
||||||
// context.read<RoutineBloc>().add(
|
|
||||||
// AddFunctionToRoutine(
|
|
||||||
// function,
|
|
||||||
// uniqueCustomId,
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
context.read<RoutineBloc>().add(
|
context.read<RoutineBloc>().add(
|
||||||
AddFunctionToRoutine(
|
AddFunctionToRoutine(
|
||||||
state.addedFunctions,
|
state.addedFunctions,
|
||||||
@ -173,24 +168,26 @@ class ThreeGangSwitchHelper {
|
|||||||
AllDevicesModel? device,
|
AllDevicesModel? device,
|
||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
|
required String dialogType,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1' ||
|
if (selectedFunction == 'countdown_1' ||
|
||||||
selectedFunction == 'countdown_2' ||
|
selectedFunction == 'countdown_2' ||
|
||||||
selectedFunction == 'countdown_3') {
|
selectedFunction == 'countdown_3') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 0;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
selectCode: selectedFunction,
|
selectCode: selectedFunction,
|
||||||
currentCondition: selectedFunctionData?.condition,
|
currentCondition: selectedFunctionData?.condition,
|
||||||
device: device,
|
device: device,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData: selectedFunctionData,
|
||||||
removeComparetors: removeComparetors,
|
removeComparetors: removeComparetors,
|
||||||
);
|
dialogType: dialogType);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
final selectedFn =
|
||||||
|
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -213,93 +210,18 @@ class ThreeGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
bool? removeComparetors,
|
bool? removeComparetors,
|
||||||
|
required String dialogType,
|
||||||
}) {
|
}) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (removeComparetors != true)
|
|
||||||
_buildConditionToggle(
|
|
||||||
context,
|
|
||||||
currentCondition,
|
|
||||||
selectCode,
|
|
||||||
device,
|
|
||||||
operationName,
|
|
||||||
selectedFunctionData,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
|
||||||
selectedFunctionData, selectCode),
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||||
selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode, dialogType),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build condition toggle for AC functions dialog
|
|
||||||
static Widget _buildConditionToggle(
|
|
||||||
BuildContext context,
|
|
||||||
String? currentCondition,
|
|
||||||
String selectCode,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
// Function(String) onConditionChanged,
|
|
||||||
) {
|
|
||||||
final conditions = ["<", "==", ">"];
|
|
||||||
|
|
||||||
return ToggleButtons(
|
|
||||||
onPressed: (int index) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
condition: conditions[index],
|
|
||||||
value: selectedFunctionData?.value ?? 0,
|
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
selectedColor: Colors.white,
|
|
||||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: 40.0,
|
|
||||||
minWidth: 40.0,
|
|
||||||
),
|
|
||||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build temperature display for AC functions dialog
|
|
||||||
static Widget _buildCountDownDisplay(
|
|
||||||
BuildContext context,
|
|
||||||
dynamic initialValue,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
String selectCode) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Widget _buildCountDownSlider(
|
static Widget _buildCountDownSlider(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
dynamic initialValue,
|
dynamic initialValue,
|
||||||
@ -307,38 +229,47 @@ class ThreeGangSwitchHelper {
|
|||||||
String operationName,
|
String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
|
String dialogType,
|
||||||
) {
|
) {
|
||||||
const twelveHoursInSeconds = 43200.0;
|
return CustomRoutinesTextbox(
|
||||||
final operationalValues = SwitchOperationalValue(
|
withSpecialChar: true,
|
||||||
icon: '',
|
currentCondition: selectedFunctionData?.condition,
|
||||||
description: "sec",
|
dialogType: dialogType,
|
||||||
value: 0.0,
|
sliderRange: (0, 43200),
|
||||||
minValue: 0,
|
displayedValue: (initialValue ?? 0).toString(),
|
||||||
maxValue: twelveHoursInSeconds,
|
initialValue: (initialValue ?? 0).toString(),
|
||||||
stepValue: 1,
|
onConditionChanged: (condition) {
|
||||||
);
|
|
||||||
return Slider(
|
|
||||||
value: (initialValue ?? 0).toDouble(),
|
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
|
||||||
divisions:
|
|
||||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
|
||||||
(operationalValues.stepValue ?? 1))
|
|
||||||
.round(),
|
|
||||||
onChanged: (value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value,
|
condition: condition,
|
||||||
|
value: selectedFunctionData?.value ?? 0,
|
||||||
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTextChanged: (value) {
|
||||||
|
final roundedValue = value.round();
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: roundedValue,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
unit: 'sec',
|
||||||
|
dividendOfRange: 1,
|
||||||
|
stepIncreaseAmount: 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,7 +305,9 @@ class ThreeGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected
|
||||||
|
? Icons.radio_button_checked
|
||||||
|
: Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -390,7 +323,8 @@ class ThreeGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription:
|
||||||
|
selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
|||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
|
||||||
@ -86,14 +87,15 @@ class TwoGangSwitchHelper {
|
|||||||
size: 16,
|
size: 16,
|
||||||
color: ColorsManager.textGray,
|
color: ColorsManager.textGray,
|
||||||
),
|
),
|
||||||
onTap: () =>
|
onTap: () => RoutineTapFunctionHelper
|
||||||
RoutineTapFunctionHelper.onTapFunction(
|
.onTapFunction(
|
||||||
context,
|
context,
|
||||||
functionCode: function.code,
|
functionCode: function.code,
|
||||||
functionOperationName:
|
functionOperationName:
|
||||||
function.operationName,
|
function.operationName,
|
||||||
functionValueDescription:
|
functionValueDescription:
|
||||||
selectedFunctionData.valueDescription,
|
selectedFunctionData
|
||||||
|
.valueDescription,
|
||||||
deviceUuid: device?.uuid,
|
deviceUuid: device?.uuid,
|
||||||
codesToAddIntoFunctionsWithDefaultValue: [
|
codesToAddIntoFunctionsWithDefaultValue: [
|
||||||
'countdown_1',
|
'countdown_1',
|
||||||
@ -115,6 +117,7 @@ class TwoGangSwitchHelper {
|
|||||||
device: device,
|
device: device,
|
||||||
operationName: selectedOperationName ?? '',
|
operationName: selectedOperationName ?? '',
|
||||||
removeComparetors: removeComparetors,
|
removeComparetors: removeComparetors,
|
||||||
|
dialogType: dialogType,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -172,22 +175,25 @@ class TwoGangSwitchHelper {
|
|||||||
AllDevicesModel? device,
|
AllDevicesModel? device,
|
||||||
required String operationName,
|
required String operationName,
|
||||||
required bool removeComparetors,
|
required bool removeComparetors,
|
||||||
|
required String dialogType,
|
||||||
}) {
|
}) {
|
||||||
if (selectedFunction == 'countdown_1' || selectedFunction == 'countdown_2') {
|
if (selectedFunction == 'countdown_1' ||
|
||||||
|
selectedFunction == 'countdown_2') {
|
||||||
final initialValue = selectedFunctionData?.value ?? 0;
|
final initialValue = selectedFunctionData?.value ?? 0;
|
||||||
return _buildTemperatureSelector(
|
return _buildTemperatureSelector(
|
||||||
context: context,
|
context: context,
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
selectCode: selectedFunction,
|
selectCode: selectedFunction,
|
||||||
currentCondition: selectedFunctionData?.condition,
|
currentCondition: selectedFunctionData?.condition,
|
||||||
device: device,
|
device: device,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
selectedFunctionData: selectedFunctionData,
|
selectedFunctionData: selectedFunctionData,
|
||||||
removeComparetors: removeComparetors,
|
removeComparetors: removeComparetors,
|
||||||
);
|
dialogType: dialogType);
|
||||||
}
|
}
|
||||||
|
|
||||||
final selectedFn = switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
final selectedFn =
|
||||||
|
switchFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
return _buildOperationalValuesList(
|
return _buildOperationalValuesList(
|
||||||
@ -210,25 +216,13 @@ class TwoGangSwitchHelper {
|
|||||||
required String operationName,
|
required String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
bool? removeComparetors,
|
bool? removeComparetors,
|
||||||
|
String? dialogType,
|
||||||
}) {
|
}) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (removeComparetors != true)
|
|
||||||
_buildConditionToggle(
|
|
||||||
context,
|
|
||||||
currentCondition,
|
|
||||||
selectCode,
|
|
||||||
device,
|
|
||||||
operationName,
|
|
||||||
selectedFunctionData,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
|
||||||
selectedFunctionData, selectCode),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||||
selectedFunctionData, selectCode),
|
selectedFunctionData, selectCode, dialogType!),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -269,7 +263,8 @@ class TwoGangSwitchHelper {
|
|||||||
minHeight: 40.0,
|
minHeight: 40.0,
|
||||||
minWidth: 40.0,
|
minWidth: 40.0,
|
||||||
),
|
),
|
||||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
isSelected:
|
||||||
|
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
children: conditions.map((c) => Text(c)).toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -304,38 +299,48 @@ class TwoGangSwitchHelper {
|
|||||||
String operationName,
|
String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
|
String dialogType,
|
||||||
) {
|
) {
|
||||||
const twelveHoursInSeconds = 43200.0;
|
return CustomRoutinesTextbox(
|
||||||
final operationalValues = SwitchOperationalValue(
|
withSpecialChar: true,
|
||||||
icon: '',
|
currentCondition: selectedFunctionData?.condition,
|
||||||
description: "sec",
|
dialogType: dialogType,
|
||||||
value: 0.0,
|
sliderRange: (0, 43200),
|
||||||
minValue: 0,
|
displayedValue: (initialValue ?? 0).toString(),
|
||||||
maxValue: twelveHoursInSeconds,
|
initialValue: (initialValue ?? 0).toString(),
|
||||||
stepValue: 1,
|
onConditionChanged: (condition) {
|
||||||
);
|
|
||||||
return Slider(
|
|
||||||
value: (initialValue ?? 0).toDouble(),
|
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
|
||||||
divisions:
|
|
||||||
(((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
|
||||||
(operationalValues.stepValue ?? 1))
|
|
||||||
.round(),
|
|
||||||
onChanged: (value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value,
|
condition: condition,
|
||||||
|
value: selectedFunctionData?.value ?? 0,
|
||||||
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTextChanged: (value) {
|
||||||
|
final roundedValue =
|
||||||
|
value.round(); // Round to nearest integer (stepSize 1)
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: roundedValue,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
unit: 'sec',
|
||||||
|
dividendOfRange: 1,
|
||||||
|
stepIncreaseAmount: 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,7 +376,9 @@ class TwoGangSwitchHelper {
|
|||||||
style: context.textTheme.bodyMedium,
|
style: context.textTheme.bodyMedium,
|
||||||
),
|
),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
isSelected
|
||||||
|
? Icons.radio_button_checked
|
||||||
|
: Icons.radio_button_unchecked,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.primaryColorWithOpacity
|
? ColorsManager.primaryColorWithOpacity
|
||||||
@ -387,7 +394,8 @@ class TwoGangSwitchHelper {
|
|||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value.value,
|
value: value.value,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription:
|
||||||
|
selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -4,8 +4,8 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
|||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
|
||||||
|
|
||||||
class WpsValueSelectorWidget extends StatelessWidget {
|
class WpsValueSelectorWidget extends StatelessWidget {
|
||||||
final String selectedFunction;
|
final String selectedFunction;
|
||||||
@ -27,11 +27,13 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
final selectedFn =
|
||||||
|
wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||||
final values = selectedFn.getOperationalValues();
|
final values = selectedFn.getOperationalValues();
|
||||||
|
|
||||||
if (_isSliderFunction(selectedFunction)) {
|
if (_isSliderFunction(selectedFunction)) {
|
||||||
return SliderValueSelector(
|
return CustomRoutinesTextbox(
|
||||||
|
withSpecialChar: false,
|
||||||
currentCondition: functionData.condition,
|
currentCondition: functionData.condition,
|
||||||
dialogType: dialogType,
|
dialogType: dialogType,
|
||||||
sliderRange: sliderRange,
|
sliderRange: sliderRange,
|
||||||
@ -48,7 +50,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
onTextChanged: (value) => context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
@ -61,6 +63,7 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
unit: _unit,
|
unit: _unit,
|
||||||
dividendOfRange: 1,
|
dividendOfRange: 1,
|
||||||
|
stepIncreaseAmount: _steps,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,4 +102,10 @@ class WpsValueSelectorWidget extends StatelessWidget {
|
|||||||
'illuminance_value' => 'Lux',
|
'illuminance_value' => 'Lux',
|
||||||
_ => '',
|
_ => '',
|
||||||
};
|
};
|
||||||
|
double get _steps => switch (functionData.functionCode) {
|
||||||
|
'presence_time' => 1,
|
||||||
|
'dis_current' => 1,
|
||||||
|
'illuminance_value' => 1,
|
||||||
|
_ => 1,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,7 @@ class _WaterHeaterDialogRoutinesState extends State<WaterHeaterDialogRoutines> {
|
|||||||
functionData: functionData,
|
functionData: functionData,
|
||||||
whFunctions: _waterHeaterFunctions,
|
whFunctions: _waterHeaterFunctions,
|
||||||
device: widget.device,
|
device: widget.device,
|
||||||
|
dialogType: widget.dialogType,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,24 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||||
import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operational_value.dart';
|
|
||||||
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart';
|
import 'package:syncrow_web/pages/routines/models/water_heater/water_heater_functions.dart';
|
||||||
|
import 'package:syncrow_web/pages/routines/widgets/custom_routines_textbox.dart';
|
||||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_operational_values_list.dart';
|
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/water_heater/water_heater_operational_values_list.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
||||||
final String selectedFunction;
|
final String selectedFunction;
|
||||||
final DeviceFunctionData functionData;
|
final DeviceFunctionData functionData;
|
||||||
final List<WaterHeaterFunctions> whFunctions;
|
final List<WaterHeaterFunctions> whFunctions;
|
||||||
final AllDevicesModel? device;
|
final AllDevicesModel? device;
|
||||||
|
final String dialogType;
|
||||||
|
|
||||||
const WaterHeaterValueSelectorWidget({
|
const WaterHeaterValueSelectorWidget({
|
||||||
required this.selectedFunction,
|
required this.selectedFunction,
|
||||||
required this.functionData,
|
required this.functionData,
|
||||||
required this.whFunctions,
|
required this.whFunctions,
|
||||||
required this.device,
|
required this.device,
|
||||||
|
required this.dialogType,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,22 +38,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
_buildConditionToggle(
|
|
||||||
context,
|
|
||||||
functionData.condition,
|
|
||||||
selectedFunction,
|
|
||||||
device,
|
|
||||||
selectedFn.operationName,
|
|
||||||
functionData,
|
|
||||||
),
|
|
||||||
_buildCountDownDisplay(
|
|
||||||
context,
|
|
||||||
functionData.value,
|
|
||||||
device,
|
|
||||||
selectedFn.operationName,
|
|
||||||
functionData,
|
|
||||||
selectedFunction,
|
|
||||||
),
|
|
||||||
_buildCountDownSlider(
|
_buildCountDownSlider(
|
||||||
context,
|
context,
|
||||||
functionData.value,
|
functionData.value,
|
||||||
@ -62,6 +45,7 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
|||||||
selectedFn.operationName,
|
selectedFn.operationName,
|
||||||
functionData,
|
functionData,
|
||||||
selectedFunction,
|
selectedFunction,
|
||||||
|
dialogType
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
],
|
],
|
||||||
@ -90,28 +74,6 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Widget _buildCountDownDisplay(
|
|
||||||
BuildContext context,
|
|
||||||
dynamic initialValue,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
String selectCode) {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
DurationFormatMixin.formatDuration(initialValue?.toInt() ?? 0),
|
|
||||||
style: context.textTheme.headlineMedium!.copyWith(
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Widget _buildCountDownSlider(
|
static Widget _buildCountDownSlider(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
dynamic initialValue,
|
dynamic initialValue,
|
||||||
@ -119,78 +81,47 @@ class WaterHeaterValueSelectorWidget extends StatelessWidget {
|
|||||||
String operationName,
|
String operationName,
|
||||||
DeviceFunctionData? selectedFunctionData,
|
DeviceFunctionData? selectedFunctionData,
|
||||||
String selectCode,
|
String selectCode,
|
||||||
|
String dialogType,
|
||||||
) {
|
) {
|
||||||
const twelveHoursInSeconds = 43200.0;
|
return CustomRoutinesTextbox(
|
||||||
final operationalValues = SwitchOperationalValue(
|
withSpecialChar: false,
|
||||||
icon: '',
|
currentCondition: selectedFunctionData?.condition,
|
||||||
description: "sec",
|
dialogType: dialogType,
|
||||||
value: 0.0,
|
sliderRange: (0, 43200),
|
||||||
minValue: 0,
|
displayedValue: (initialValue ?? 0).toString(),
|
||||||
maxValue: twelveHoursInSeconds,
|
initialValue: (initialValue ?? 0).toString(),
|
||||||
stepValue: 1,
|
onConditionChanged: (condition) {
|
||||||
);
|
|
||||||
return Slider(
|
|
||||||
value: (initialValue ?? 0).toDouble(),
|
|
||||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
|
||||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
|
||||||
divisions: (((operationalValues.maxValue ?? 0) -
|
|
||||||
(operationalValues.minValue ?? 0)) /
|
|
||||||
(operationalValues.stepValue ?? 1))
|
|
||||||
.round(),
|
|
||||||
onChanged: (value) {
|
|
||||||
context.read<FunctionBloc>().add(
|
context.read<FunctionBloc>().add(
|
||||||
AddFunction(
|
AddFunction(
|
||||||
functionData: DeviceFunctionData(
|
functionData: DeviceFunctionData(
|
||||||
entityId: device?.uuid ?? '',
|
entityId: device?.uuid ?? '',
|
||||||
functionCode: selectCode,
|
functionCode: selectCode,
|
||||||
operationName: operationName,
|
operationName: operationName,
|
||||||
value: value,
|
value: condition,
|
||||||
|
condition: condition,
|
||||||
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onTextChanged: (value) {
|
||||||
|
final roundedValue = value.round();
|
||||||
|
context.read<FunctionBloc>().add(
|
||||||
|
AddFunction(
|
||||||
|
functionData: DeviceFunctionData(
|
||||||
|
entityId: device?.uuid ?? '',
|
||||||
|
functionCode: selectCode,
|
||||||
|
operationName: operationName,
|
||||||
|
value: roundedValue,
|
||||||
condition: selectedFunctionData?.condition,
|
condition: selectedFunctionData?.condition,
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
valueDescription: selectedFunctionData?.valueDescription,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
unit: 'sec',
|
||||||
}
|
dividendOfRange: 1,
|
||||||
|
stepIncreaseAmount: 1,
|
||||||
static Widget _buildConditionToggle(
|
|
||||||
BuildContext context,
|
|
||||||
String? currentCondition,
|
|
||||||
String selectCode,
|
|
||||||
AllDevicesModel? device,
|
|
||||||
String operationName,
|
|
||||||
DeviceFunctionData? selectedFunctionData,
|
|
||||||
) {
|
|
||||||
final conditions = ["<", "==", ">"];
|
|
||||||
|
|
||||||
return ToggleButtons(
|
|
||||||
onPressed: (int index) {
|
|
||||||
context.read<FunctionBloc>().add(
|
|
||||||
AddFunction(
|
|
||||||
functionData: DeviceFunctionData(
|
|
||||||
entityId: device?.uuid ?? '',
|
|
||||||
functionCode: selectCode,
|
|
||||||
operationName: operationName,
|
|
||||||
condition: conditions[index],
|
|
||||||
value: selectedFunctionData?.value ?? 0,
|
|
||||||
valueDescription: selectedFunctionData?.valueDescription,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
|
||||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
selectedColor: Colors.white,
|
|
||||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
|
||||||
color: ColorsManager.primaryColorWithOpacity,
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
minHeight: 40.0,
|
|
||||||
minWidth: 40.0,
|
|
||||||
),
|
|
||||||
isSelected:
|
|
||||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
|
||||||
children: conditions.map((c) => Text(c)).toList(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@ import 'package:syncrow_web/services/space_mana_api.dart';
|
|||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action;
|
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 CommunitySpaceManagementApi _api;
|
||||||
final ProductApi _productApi;
|
final ProductApi _productApi;
|
||||||
final SpaceModelManagementApi _spaceModelApi;
|
final SpaceModelManagementApi _spaceModelApi;
|
||||||
@ -62,7 +63,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
int page = 1;
|
int page = 1;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
final spaceModels = await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||||
|
page: page, projectId: projectUuid);
|
||||||
if (spaceModels.isNotEmpty) {
|
if (spaceModels.isNotEmpty) {
|
||||||
allSpaceModels.addAll(spaceModels);
|
allSpaceModels.addAll(spaceModels);
|
||||||
page++;
|
page++;
|
||||||
@ -75,26 +77,29 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceModelLoaded(
|
emit(SpaceModelLoaded(
|
||||||
communities:
|
communities: state is SpaceManagementLoaded
|
||||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
? (state as SpaceManagementLoaded).communities
|
||||||
|
: [],
|
||||||
products: _cachedProducts ?? [],
|
products: _cachedProducts ?? [],
|
||||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteSpaceModelFromCache(
|
void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event,
|
||||||
DeleteSpaceModelFromCache event, Emitter<SpaceManagementState> emit) async {
|
Emitter<SpaceManagementState> emit) async {
|
||||||
if (_cachedSpaceModels != null) {
|
if (_cachedSpaceModels != null) {
|
||||||
_cachedSpaceModels =
|
_cachedSpaceModels = _cachedSpaceModels!
|
||||||
_cachedSpaceModels!.where((model) => model.uuid != event.deletedUuid).toList();
|
.where((model) => model.uuid != event.deletedUuid)
|
||||||
|
.toList();
|
||||||
} else {
|
} else {
|
||||||
_cachedSpaceModels = await fetchSpaceModels();
|
_cachedSpaceModels = await fetchSpaceModels();
|
||||||
}
|
}
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceModelLoaded(
|
emit(SpaceModelLoaded(
|
||||||
communities:
|
communities: state is SpaceManagementLoaded
|
||||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
? (state as SpaceManagementLoaded).communities
|
||||||
|
: [],
|
||||||
products: _cachedProducts ?? [],
|
products: _cachedProducts ?? [],
|
||||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
@ -122,8 +127,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
int page = 1;
|
int page = 1;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
final spaceModels =
|
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||||
await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
page: page, projectId: projectUuid);
|
||||||
if (spaceModels.isNotEmpty) {
|
if (spaceModels.isNotEmpty) {
|
||||||
allSpaceModels.addAll(spaceModels);
|
allSpaceModels.addAll(spaceModels);
|
||||||
page++;
|
page++;
|
||||||
@ -164,10 +169,12 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceManagementLoading());
|
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 (success) {
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities);
|
final updatedCommunities =
|
||||||
|
List<CommunityModel>.from(previousState.communities);
|
||||||
for (var community in updatedCommunities) {
|
for (var community in updatedCommunities) {
|
||||||
if (community.uuid == event.communityUuid) {
|
if (community.uuid == event.communityUuid) {
|
||||||
community.name = event.name;
|
community.name = event.name;
|
||||||
@ -212,7 +219,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(String communityUuid) async {
|
Future<List<SpaceModel>> _fetchSpacesForCommunity(
|
||||||
|
String communityUuid) async {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
||||||
@ -242,20 +250,23 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onBlankState(BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
Future<void> _onBlankState(
|
||||||
|
BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||||
try {
|
try {
|
||||||
final previousState = state;
|
final previousState = state;
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
List<CommunityModel> communities =
|
||||||
|
await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
await fetchSpaceModels();
|
await fetchSpaceModels();
|
||||||
await fetchTags();
|
// await fetchTags();
|
||||||
|
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
if (previousState is SpaceManagementLoaded ||
|
||||||
|
previousState is BlankState) {
|
||||||
final prevCommunities = (previousState as dynamic).communities ?? [];
|
final prevCommunities = (previousState as dynamic).communities ?? [];
|
||||||
emit(BlankState(
|
emit(BlankState(
|
||||||
communities: List<CommunityModel>.from(prevCommunities),
|
communities: List<CommunityModel>.from(prevCommunities),
|
||||||
@ -286,7 +297,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
_onloadProducts();
|
_onloadProducts();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
// Wait until `communityList` is loaded
|
// 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
|
// Fetch space models after communities are available
|
||||||
final prevSpaceModels = await fetchSpaceModels();
|
final prevSpaceModels = await fetchSpaceModels();
|
||||||
@ -310,8 +322,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
final completer = Completer<List<CommunityModel>>();
|
final completer = Completer<List<CommunityModel>>();
|
||||||
final subscription = spaceBloc.stream.listen((state) {
|
final subscription = spaceBloc.stream.listen((state) {
|
||||||
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
||||||
completer
|
completer.complete(state.searchQuery.isNotEmpty
|
||||||
.complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList);
|
? state.filteredCommunity
|
||||||
|
: state.communityList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@ -339,7 +352,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
emit(SpaceManagementLoading());
|
emit(SpaceManagementLoading());
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
final success = await _api.deleteCommunity(event.communityUuid, projectUuid);
|
final success =
|
||||||
|
await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||||
if (success) {
|
if (success) {
|
||||||
// add(LoadCommunityAndSpacesEvent());
|
// add(LoadCommunityAndSpacesEvent());
|
||||||
} else {
|
} else {
|
||||||
@ -361,12 +375,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
CommunityModel? newCommunity =
|
CommunityModel? newCommunity = await _api.createCommunity(
|
||||||
await _api.createCommunity(event.name, event.description, projectUuid);
|
event.name, event.description, projectUuid);
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
if (newCommunity != null) {
|
if (newCommunity != null) {
|
||||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
if (previousState is SpaceManagementLoaded ||
|
||||||
|
previousState is BlankState) {
|
||||||
final prevCommunities = List<CommunityModel>.from(
|
final prevCommunities = List<CommunityModel>.from(
|
||||||
(previousState as dynamic).communities,
|
(previousState as dynamic).communities,
|
||||||
);
|
);
|
||||||
@ -459,12 +474,15 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
try {
|
try {
|
||||||
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
final updatedSpaces =
|
final updatedSpaces = await saveSpacesHierarchically(
|
||||||
await saveSpacesHierarchically(event.context, event.spaces, event.communityUuid);
|
event.context, event.spaces, event.communityUuid);
|
||||||
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
||||||
|
|
||||||
emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
// emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
||||||
|
// updatedSpaces.forEach(
|
||||||
|
// (element) => element.uuid,
|
||||||
|
// );
|
||||||
|
// final lastUpdatedSpaced = updatedSpaces..addAll(allSpaces);
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
await _updateLoadedState(
|
await _updateLoadedState(
|
||||||
spaceTreeState,
|
spaceTreeState,
|
||||||
@ -475,7 +493,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(SpaceManagementError('Error saving spaces: $e'));
|
// emit(SpaceManagementError('Error saving spaces: $e'));
|
||||||
|
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
emit(previousState);
|
emit(previousState);
|
||||||
@ -515,13 +533,15 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit(previousState);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
rethrow;
|
emit(previousState);
|
||||||
|
// rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
Future<List<SpaceModel>> saveSpacesHierarchically(BuildContext context,
|
||||||
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
List<SpaceModel> spaces, String communityUuid) async {
|
||||||
final orderedSpaces = flattenHierarchy(spaces);
|
final orderedSpaces = flattenHierarchy(spaces);
|
||||||
|
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -534,6 +554,14 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
selectedCommunity = filteredCommunities.firstWhere(
|
selectedCommunity = filteredCommunities.firstWhere(
|
||||||
(community) => community.uuid == communityUuid,
|
(community) => community.uuid == communityUuid,
|
||||||
|
orElse: () => CommunityModel(
|
||||||
|
uuid: '',
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
spaces: spaces,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return [];
|
return [];
|
||||||
@ -548,9 +576,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
if (parent.uuid != null) {
|
if (parent.uuid != null) {
|
||||||
await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid);
|
await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
orderedSpaces.removeWhere((space) => parentsToDelete.contains(space));
|
orderedSpaces.removeWhere((space) => parentsToDelete.contains(space));
|
||||||
|
|
||||||
@ -564,7 +590,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
if (matchedSpaces.isEmpty) continue;
|
if (matchedSpaces.isEmpty) continue;
|
||||||
|
|
||||||
final prevSpace = matchedSpaces[0];
|
final prevSpace = matchedSpaces.elementAtOrNull(0);
|
||||||
|
|
||||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||||
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
||||||
@ -575,17 +601,19 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
if (prevSubspaces != null || newSubspaces != null) {
|
if (prevSubspaces != null || newSubspaces != null) {
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
if (prevSubspaces != null && newSubspaces != null) {
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
final existsInNew =
|
final existsInNew = newSubspaces
|
||||||
newSubspaces.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
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) {
|
} else if (prevSubspaces != null && newSubspaces == null) {
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
action: custom_action.Action.delete, uuid: prevSubspace.uuid));
|
action: custom_action.Action.delete,
|
||||||
|
uuid: prevSubspace.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +641,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
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) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||||
@ -639,9 +669,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
subspaces: subspaceUpdates,
|
subspaces: space.subspaces,
|
||||||
tags: tagUpdates,
|
// subspaceUpdates,
|
||||||
direction: space.incomingConnection?.direction,
|
tags: space.tags,
|
||||||
|
// tagUpdates,
|
||||||
spaceModelUuid: space.spaceModel?.uuid,
|
spaceModelUuid: space.spaceModel?.uuid,
|
||||||
projectId: projectUuid);
|
projectId: projectUuid);
|
||||||
} else {
|
} else {
|
||||||
@ -651,8 +682,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||||
final tagBodyModels =
|
final tagBodyModels = subspace.tags
|
||||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
?.map((tag) => tag.toCreateTagBodyModel())
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
return CreateSubspaceModel()
|
return CreateSubspaceModel()
|
||||||
..subspaceName = subspace.subspaceName
|
..subspaceName = subspace.subspaceName
|
||||||
..tags = tagBodyModels;
|
..tags = tagBodyModels;
|
||||||
@ -671,7 +704,6 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
direction: space.incomingConnection?.direction,
|
|
||||||
spaceModelUuid: space.spaceModel?.uuid,
|
spaceModelUuid: space.spaceModel?.uuid,
|
||||||
tags: tagBodyModels,
|
tags: tagBodyModels,
|
||||||
subspaces: createSubspaceBodyModels,
|
subspaces: createSubspaceBodyModels,
|
||||||
@ -679,7 +711,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
space.uuid = response?.uuid;
|
space.uuid = response?.uuid;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow; // Stop further execution on failure
|
return [];
|
||||||
|
// Stop further execution on failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spaces;
|
return spaces;
|
||||||
@ -710,7 +743,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return result.toList(); // Convert back to a list
|
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());
|
emit(SpaceManagementLoading());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -757,14 +791,17 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
// Case 1: Tags deleted
|
// Case 1: Tags deleted
|
||||||
if (prevTags != null && newTags != null) {
|
if (prevTags != null && newTags != null) {
|
||||||
for (var prevTag in prevTags) {
|
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) {
|
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) {
|
} else if (prevTags != null && newTags == null) {
|
||||||
for (var prevTag in prevTags) {
|
for (var prevTag in prevTags) {
|
||||||
tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid));
|
tagUpdates.add(TagModelUpdate(
|
||||||
|
action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,15 +844,16 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return tagUpdates;
|
return tagUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SpaceModel> findMatchingSpaces(List<SpaceModel> spaces, String targetUuid) {
|
List<SpaceModel> findMatchingSpaces(
|
||||||
|
List<SpaceModel> spaces, String targetUuid) {
|
||||||
List<SpaceModel> matched = [];
|
List<SpaceModel> matched = [];
|
||||||
|
|
||||||
for (var space in spaces) {
|
for (var space in spaces) {
|
||||||
if (space.uuid == targetUuid) {
|
if (space.uuid == targetUuid) {
|
||||||
matched.add(space);
|
matched.add(space);
|
||||||
}
|
}
|
||||||
matched
|
matched.addAll(findMatchingSpaces(
|
||||||
.addAll(findMatchingSpaces(space.children, targetUuid)); // Recursively search in children
|
space.children, targetUuid)); // Recursively search in children
|
||||||
}
|
}
|
||||||
|
|
||||||
return matched;
|
return matched;
|
||||||
|
@ -3,23 +3,26 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
|
|||||||
class Connection {
|
class Connection {
|
||||||
final SpaceModel startSpace;
|
final SpaceModel startSpace;
|
||||||
final SpaceModel endSpace;
|
final SpaceModel endSpace;
|
||||||
final String direction;
|
|
||||||
|
|
||||||
Connection({required this.startSpace, required this.endSpace, required this.direction});
|
Connection({
|
||||||
|
required this.startSpace,
|
||||||
|
required this.endSpace,
|
||||||
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'startUuid': startSpace.uuid ?? 'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces
|
'startUuid': startSpace.uuid ??
|
||||||
'endUuid': endSpace.uuid ?? 'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces
|
'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces
|
||||||
'direction': direction,
|
'endUuid': endSpace.uuid ??
|
||||||
|
'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Connection fromMap(Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
static Connection fromMap(
|
||||||
|
Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
||||||
return Connection(
|
return Connection(
|
||||||
startSpace: spaces[map['startUuid']]!,
|
startSpace: spaces[map['startUuid']]!,
|
||||||
endSpace: spaces[map['endUuid']]!,
|
endSpace: spaces[map['endUuid']]!,
|
||||||
direction: map['direction'],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
import 'selected_product_model.dart';
|
||||||
|
|
||||||
class ProductModel {
|
class ProductModel {
|
||||||
final String uuid;
|
final String uuid;
|
||||||
final String catName;
|
final String catName;
|
||||||
@ -38,6 +40,15 @@ class ProductModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectedProduct toSelectedProduct(int count) {
|
||||||
|
return SelectedProduct(
|
||||||
|
productId: uuid,
|
||||||
|
count: count,
|
||||||
|
productName: name!,
|
||||||
|
product: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static String _mapIconToProduct(String prodType) {
|
static String _mapIconToProduct(String prodType) {
|
||||||
const iconMapping = {
|
const iconMapping = {
|
||||||
'1G': Assets.Gang1SwitchIcon,
|
'1G': Assets.Gang1SwitchIcon,
|
||||||
|
@ -101,7 +101,7 @@ class SpaceModel {
|
|||||||
spaceModel: json['spaceModel'] != null
|
spaceModel: json['spaceModel'] != null
|
||||||
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
||||||
: null,
|
: null,
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['productAllocations'] as List<dynamic>?)
|
||||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
?.where((item) => item is Map<String, dynamic>) // Validate type
|
||||||
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
@ -116,7 +116,6 @@ class SpaceModel {
|
|||||||
instance.incomingConnection = Connection(
|
instance.incomingConnection = Connection(
|
||||||
startSpace: instance.parent ?? instance, // Parent space
|
startSpace: instance.parent ?? instance, // Parent space
|
||||||
endSpace: instance, // This space instance
|
endSpace: instance, // This space instance
|
||||||
direction: conn['direction'],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class SubspaceModel {
|
|||||||
subspaceName: json['subspaceName'] ?? '',
|
subspaceName: json['subspaceName'] ?? '',
|
||||||
disabled: json['disabled'] ?? false,
|
disabled: json['disabled'] ?? false,
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['productAllocations'] as List<dynamic>?)
|
||||||
?.map((item) => Tag.fromJson(item))
|
?.map((item) => Tag.fromJson(item))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
@ -36,7 +36,7 @@ class SubspaceModel {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'uuid': uuid,
|
if (uuid != null) 'uuid': uuid,
|
||||||
'subspaceName': subspaceName,
|
'subspaceName': subspaceName,
|
||||||
'disabled': disabled,
|
'disabled': disabled,
|
||||||
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
||||||
|
@ -23,10 +23,13 @@ class Tag extends BaseTag {
|
|||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||||
|
|
||||||
return Tag(
|
return Tag(
|
||||||
uuid: json['uuid'] ?? '',
|
//TODO:insure UUId for tag or prodAlloc
|
||||||
|
uuid: json['name'] != null ? json['uuid'] : json['tag']?['uuid'] ?? '',
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
tag: json['name'] ?? '',
|
tag: json['name'] ?? json['tag']?['name'] ?? '',
|
||||||
product: json['product'] != null ? ProductModel.fromMap(json['product']) : null,
|
product: json['product'] != null
|
||||||
|
? ProductModel.fromMap(json['product'])
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +52,10 @@ class Tag extends BaseTag {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'uuid': uuid,
|
if (uuid != null) 'uuid': uuid,
|
||||||
'tag': tag,
|
'name': tag,
|
||||||
'product': product?.toMap(),
|
'productUuid': product?.uuid,
|
||||||
|
// .toMap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
_nameController = TextEditingController(
|
_nameController = TextEditingController(
|
||||||
text: widget.selectedCommunity?.name ?? '',
|
text: widget.selectedCommunity?.name ?? '',
|
||||||
@ -96,13 +97,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
if (oldWidget.spaces != widget.spaces) {
|
if (oldWidget.spaces != widget.spaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
realignTree();
|
realignTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||||
|
widget.selectedSpace != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_moveToSpace(widget.selectedSpace!);
|
_moveToSpace(widget.selectedSpace!);
|
||||||
});
|
});
|
||||||
@ -185,7 +188,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
connection, widget.selectedSpace)
|
connection, widget.selectedSpace)
|
||||||
? 1.0
|
? 1.0
|
||||||
: 0.3, // Adjust opacity
|
: 0.3, // Adjust opacity
|
||||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
child: CustomPaint(
|
||||||
|
painter: CurvedLinePainter([connection])),
|
||||||
),
|
),
|
||||||
for (var entry in spaces.asMap().entries)
|
for (var entry in spaces.asMap().entries)
|
||||||
if (entry.value.status != SpaceStatus.deleted &&
|
if (entry.value.status != SpaceStatus.deleted &&
|
||||||
@ -195,11 +199,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
top: entry.value.position.dy,
|
top: entry.value.position.dy,
|
||||||
child: SpaceCardWidget(
|
child: SpaceCardWidget(
|
||||||
index: entry.key,
|
index: entry.key,
|
||||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
onButtonTap: (int index, Offset newPosition) {
|
||||||
_showCreateSpaceDialog(screenSize,
|
_showCreateSpaceDialog(screenSize,
|
||||||
position: spaces[index].position + newPosition,
|
position:
|
||||||
|
spaces[index].position + newPosition,
|
||||||
parentIndex: index,
|
parentIndex: index,
|
||||||
direction: direction,
|
|
||||||
projectTags: widget.projectTags);
|
projectTags: widget.projectTags);
|
||||||
},
|
},
|
||||||
position: entry.value.position,
|
position: entry.value.position,
|
||||||
@ -210,8 +214,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
_updateNodePosition(entry.value, newPosition);
|
_updateNodePosition(entry.value, newPosition);
|
||||||
},
|
},
|
||||||
buildSpaceContainer: (int index) {
|
buildSpaceContainer: (int index) {
|
||||||
final bool isHighlighted = SpaceHelper.isHighlightedSpace(
|
final bool isHighlighted =
|
||||||
spaces[index], widget.selectedSpace);
|
SpaceHelper.isHighlightedSpace(
|
||||||
|
spaces[index], widget.selectedSpace);
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: isHighlighted ? 1.0 : 0.3,
|
opacity: isHighlighted ? 1.0 : 0.3,
|
||||||
@ -289,7 +294,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
void _showCreateSpaceDialog(Size screenSize,
|
void _showCreateSpaceDialog(Size screenSize,
|
||||||
{Offset? position,
|
{Offset? position,
|
||||||
int? parentIndex,
|
int? parentIndex,
|
||||||
String? direction,
|
|
||||||
double? canvasWidth,
|
double? canvasWidth,
|
||||||
double? canvasHeight,
|
double? canvasHeight,
|
||||||
required List<Tag> projectTags}) {
|
required List<Tag> projectTags}) {
|
||||||
@ -299,19 +303,25 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return CreateSpaceDialog(
|
return CreateSpaceDialog(
|
||||||
products: widget.products,
|
products: widget.products,
|
||||||
spaceModels: widget.spaceModels,
|
spaceModels: widget.spaceModels,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags:
|
||||||
|
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
onCreateSpace: (String name,
|
||||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
String icon,
|
||||||
|
List<SelectedProduct> selectedProducts,
|
||||||
|
SpaceTemplateModel? spaceModel,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Set the first space in the center or use passed position
|
// Set the first space in the center or use passed position
|
||||||
Offset newPosition;
|
Offset newPosition;
|
||||||
if (parentIndex != null) {
|
if (parentIndex != null) {
|
||||||
newPosition =
|
newPosition = getBalancedChildPosition(
|
||||||
getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position
|
spaces[parentIndex]); // Ensure balanced position
|
||||||
} else {
|
} else {
|
||||||
newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
newPosition =
|
||||||
|
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel newSpace = SpaceModel(
|
SpaceModel newSpace = SpaceModel(
|
||||||
@ -325,14 +335,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
tags: tags);
|
tags: tags);
|
||||||
|
|
||||||
if (parentIndex != null && direction != null) {
|
if (parentIndex != null) {
|
||||||
SpaceModel parentSpace = spaces[parentIndex];
|
SpaceModel parentSpace = spaces[parentIndex];
|
||||||
parentSpace.internalId = spaces[parentIndex].internalId;
|
parentSpace.internalId = spaces[parentIndex].internalId;
|
||||||
newSpace.parent = parentSpace;
|
newSpace.parent = parentSpace;
|
||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: parentSpace,
|
startSpace: parentSpace,
|
||||||
endSpace: newSpace,
|
endSpace: newSpace,
|
||||||
direction: direction,
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
newSpace.incomingConnection = newConnection;
|
newSpace.incomingConnection = newConnection;
|
||||||
@ -360,16 +369,21 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
name: widget.selectedSpace!.name,
|
name: widget.selectedSpace!.name,
|
||||||
icon: widget.selectedSpace!.icon,
|
icon: widget.selectedSpace!.icon,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
parentSpace:
|
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||||
SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces),
|
widget.selectedSpace?.parent?.internalId, spaces),
|
||||||
editSpace: widget.selectedSpace,
|
editSpace: widget.selectedSpace,
|
||||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||||
tags: widget.selectedSpace?.tags,
|
tags: widget.selectedSpace?.tags,
|
||||||
subspaces: widget.selectedSpace?.subspaces,
|
subspaces: widget.selectedSpace?.subspaces,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags: TagHelper.getAllTagValues(
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
widget.communities, widget.spaceModels),
|
||||||
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(() {
|
setState(() {
|
||||||
// Update the space's properties
|
// Update the space's properties
|
||||||
widget.selectedSpace!.name = name;
|
widget.selectedSpace!.name = name;
|
||||||
@ -379,7 +393,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
widget.selectedSpace!.tags = tags;
|
widget.selectedSpace!.tags = tags;
|
||||||
|
|
||||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
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) {
|
for (var space in spaces) {
|
||||||
@ -410,7 +425,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
Map<String, SpaceModel> idToSpace = {};
|
Map<String, SpaceModel> idToSpace = {};
|
||||||
|
|
||||||
void flatten(SpaceModel space) {
|
void flatten(SpaceModel space) {
|
||||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
if (space.status == SpaceStatus.deleted ||
|
||||||
|
space.status == SpaceStatus.parentDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.add(space);
|
result.add(space);
|
||||||
@ -447,7 +463,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
Connection(
|
Connection(
|
||||||
startSpace: parent,
|
startSpace: parent,
|
||||||
endSpace: child,
|
endSpace: child,
|
||||||
direction: "down",
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -532,13 +547,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity,
|
||||||
|
selectedSpace: space),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deselectSpace(BuildContext context) {
|
void _deselectSpace(BuildContext context) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +726,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
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 = [];
|
List<SpaceModel> duplicatedSubtree = [];
|
||||||
void collectSubtree(SpaceModel node) {
|
void collectSubtree(SpaceModel node) {
|
||||||
duplicatedSubtree.add(node);
|
duplicatedSubtree.add(node);
|
||||||
@ -726,7 +745,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: parent,
|
startSpace: parent,
|
||||||
endSpace: duplicated,
|
endSpace: duplicated,
|
||||||
direction: "down",
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
duplicated.incomingConnection = newConnection;
|
duplicated.incomingConnection = newConnection;
|
||||||
@ -739,7 +757,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
final duplicatedName =
|
||||||
|
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
|
|
||||||
final newSpace = SpaceModel(
|
final newSpace = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
@ -761,7 +780,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: newSpace,
|
startSpace: newSpace,
|
||||||
endSpace: duplicatedChild,
|
endSpace: duplicatedChild,
|
||||||
direction: "down",
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
|
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
import '../../../../../common/edit_chip.dart';
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
import '../../model/subspace_model.dart';
|
||||||
|
import '../../model/tag.dart';
|
||||||
|
|
||||||
|
class DevicesPartWidget extends StatelessWidget {
|
||||||
|
const DevicesPartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.tags,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.onEditChip,
|
||||||
|
required this.onTextButtonPressed,
|
||||||
|
required this.isTagsAndSubspaceModelDisabled,
|
||||||
|
});
|
||||||
|
final bool isTagsAndSubspaceModelDisabled;
|
||||||
|
final void Function() onEditChip;
|
||||||
|
final void Function() onTextButtonPressed;
|
||||||
|
final double screenWidth;
|
||||||
|
final List<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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../../utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class IconChoosePartWidget extends StatelessWidget {
|
||||||
|
const IconChoosePartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.selectedIcon,
|
||||||
|
required this.showIconSelection,
|
||||||
|
required this.screenWidth,
|
||||||
|
});
|
||||||
|
final double screenWidth;
|
||||||
|
final String selectedIcon;
|
||||||
|
final void Function() showIconSelection;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 50),
|
||||||
|
Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: screenWidth * 0.1,
|
||||||
|
height: screenWidth * 0.1,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SvgPicture.asset(
|
||||||
|
selectedIcon,
|
||||||
|
width: screenWidth * 0.04,
|
||||||
|
height: screenWidth * 0.04,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 20,
|
||||||
|
right: 20,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: showIconSelection,
|
||||||
|
child: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.iconEdit,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../../utils/constants/assets.dart';
|
||||||
|
import '../../../space_model/models/space_template_model.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
|
||||||
|
class SpaceModelLinkingWidget extends StatelessWidget {
|
||||||
|
const SpaceModelLinkingWidget({
|
||||||
|
super.key,
|
||||||
|
required this.onDeleted,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.selectedSpaceModel,
|
||||||
|
required this.isSpaceModelDisabled,
|
||||||
|
});
|
||||||
|
final bool isSpaceModelDisabled;
|
||||||
|
final void Function()? onDeleted;
|
||||||
|
final void Function()? onPressed;
|
||||||
|
final double screenWidth;
|
||||||
|
final SpaceTemplateModel? selectedSpaceModel;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
selectedSpaceModel == null
|
||||||
|
? TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
svgAssets: Assets.link,
|
||||||
|
label: 'Link a space model',
|
||||||
|
disabled: isSpaceModelDisabled,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
width: screenWidth * 0.25,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10.0, horizontal: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
Chip(
|
||||||
|
label: Text(
|
||||||
|
selectedSpaceModel?.modelName ?? '',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.transparentColor,
|
||||||
|
width: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
deleteIcon: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 16,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleted: onDeleted),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../common/edit_chip.dart';
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
import '../../../space_model/widgets/subspace_name_label_widget.dart';
|
||||||
|
import '../../model/subspace_model.dart';
|
||||||
|
|
||||||
|
class SubSpacePartWidget extends StatefulWidget {
|
||||||
|
SubSpacePartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.isTagsAndSubspaceModelDisabled,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.editChipOnTap,
|
||||||
|
});
|
||||||
|
double screenWidth;
|
||||||
|
bool isTagsAndSubspaceModelDisabled;
|
||||||
|
final void Function() editChipOnTap;
|
||||||
|
List<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,28 +30,13 @@ class CurvedLinePainter extends CustomPainter {
|
|||||||
Offset end = connection.endSpace.position +
|
Offset end = connection.endSpace.position +
|
||||||
const Offset(75, 0); // Center top of end space
|
const Offset(75, 0); // Center top of end space
|
||||||
|
|
||||||
if (connection.direction == 'down') {
|
// Curved line for down connections
|
||||||
// Curved line for down connections
|
final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50);
|
||||||
final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50);
|
final path = Path()
|
||||||
final path = Path()
|
..moveTo(start.dx, start.dy)
|
||||||
..moveTo(start.dx, start.dy)
|
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
|
||||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
|
canvas.drawPath(path, paint);
|
||||||
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;
|
final dotPaint = Paint()..color = ColorsManager.blackColor;
|
||||||
canvas.drawCircle(start, 5, dotPaint); // Start dot
|
canvas.drawCircle(start, 5, dotPaint); // Start dot
|
||||||
canvas.drawCircle(end, 5, dotPaint); // End dot
|
canvas.drawCircle(end, 5, dotPaint); // End dot
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
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/cancel_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
||||||
@ -9,6 +7,11 @@ 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/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/subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/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/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/assign_tag/views/assign_tag_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
||||||
@ -16,8 +19,6 @@ import 'package:syncrow_web/pages/spaces_management/helper/space_helper.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/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/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/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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
||||||
@ -82,8 +83,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
selectedIcon = widget.icon ?? Assets.location;
|
selectedIcon = widget.icon ?? Assets.location;
|
||||||
nameController = TextEditingController(text: widget.name ?? '');
|
nameController = TextEditingController(text: widget.name ?? '');
|
||||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
selectedProducts =
|
||||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||||
|
isOkButtonEnabled =
|
||||||
|
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||||
if (widget.currentSpaceModel != null) {
|
if (widget.currentSpaceModel != null) {
|
||||||
subspaces = [];
|
subspaces = [];
|
||||||
tags = [];
|
tags = [];
|
||||||
@ -96,13 +99,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isSpaceModelDisabled =
|
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||||
(tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty);
|
subspaces != null && subspaces!.isNotEmpty);
|
||||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||||
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
return AlertDialog(
|
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,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: screenWidth * 0.5,
|
width: screenWidth * 0.5,
|
||||||
@ -112,50 +117,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Column(
|
child: IconChoosePartWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
selectedIcon: selectedIcon,
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
showIconSelection: _showIconSelectionDialog,
|
||||||
children: [
|
screenWidth: screenWidth,
|
||||||
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),
|
const SizedBox(width: 20),
|
||||||
@ -164,342 +129,146 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SpaceNameTextfieldWidget(
|
||||||
width: screenWidth * 0.25,
|
isNameFieldExist: isNameFieldExist,
|
||||||
child: TextField(
|
isNameFieldInvalid: isNameFieldInvalid,
|
||||||
controller: nameController,
|
nameController: nameController,
|
||||||
onChanged: (value) {
|
screenWidth: screenWidth,
|
||||||
enteredName = value.trim();
|
onChange: (value) {
|
||||||
setState(() {
|
enteredName = value.trim();
|
||||||
isNameFieldExist = false;
|
setState(() {
|
||||||
isOkButtonEnabled = false;
|
isNameFieldExist = false;
|
||||||
isNameFieldInvalid = value.isEmpty;
|
isOkButtonEnabled = false;
|
||||||
|
isNameFieldInvalid = value.isEmpty;
|
||||||
|
|
||||||
if (!isNameFieldInvalid) {
|
if (!isNameFieldInvalid) {
|
||||||
if (SpaceHelper.isNameConflict(
|
if (SpaceHelper.isNameConflict(
|
||||||
value, widget.parentSpace, widget.editSpace)) {
|
value, widget.parentSpace, widget.editSpace)) {
|
||||||
isNameFieldExist = true;
|
isNameFieldExist = true;
|
||||||
isOkButtonEnabled = false;
|
isOkButtonEnabled = false;
|
||||||
} else {
|
} else {
|
||||||
isNameFieldExist = false;
|
isNameFieldExist = false;
|
||||||
isOkButtonEnabled = true;
|
isOkButtonEnabled = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
},
|
});
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
},
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Please enter the name',
|
|
||||||
hintStyle: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.lightGrayColor),
|
|
||||||
filled: true,
|
|
||||||
fillColor: ColorsManager.boxColor,
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: isNameFieldInvalid || isNameFieldExist
|
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.boxColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (isNameFieldInvalid)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
'*Space name should not be empty.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isNameFieldExist)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
'*Name already exist',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
selectedSpaceModel == null
|
// SpaceModelLinkingWidget(
|
||||||
? TextButton(
|
// isSpaceModelDisabled: true,
|
||||||
style: TextButton.styleFrom(
|
// // isSpaceModelDisabled,
|
||||||
padding: EdgeInsets.zero,
|
// onPressed: () {
|
||||||
),
|
// isSpaceModelDisabled
|
||||||
onPressed: () {
|
// ? null
|
||||||
isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context);
|
// : _showLinkSpaceModelDialog(context);
|
||||||
},
|
// },
|
||||||
child: ButtonContentWidget(
|
// onDeleted: () => setState(() {
|
||||||
svgAssets: Assets.link,
|
// selectedSpaceModel = null;
|
||||||
label: 'Link a space model',
|
// subspaces = widget.subspaces ?? [];
|
||||||
disabled: isSpaceModelDisabled,
|
// tags = widget.tags ?? [];
|
||||||
),
|
// }),
|
||||||
)
|
// screenWidth: screenWidth,
|
||||||
: Container(
|
// selectedSpaceModel: selectedSpaceModel,
|
||||||
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),
|
const SizedBox(height: 25),
|
||||||
Row(
|
// Row(
|
||||||
children: [
|
// children: [
|
||||||
const Expanded(
|
// const Expanded(
|
||||||
child: Divider(
|
// child: Divider(
|
||||||
color: ColorsManager.neutralGray,
|
// color: ColorsManager.neutralGray,
|
||||||
thickness: 1.0,
|
// thickness: 1.0,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
// padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
child: Text(
|
// child: Text(
|
||||||
'OR',
|
// 'OR',
|
||||||
style: Theme.of(context)
|
// style: Theme.of(context)
|
||||||
.textTheme
|
// .textTheme
|
||||||
.bodyMedium
|
// .bodyMedium
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
// ?.copyWith(fontWeight: FontWeight.bold),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
const Expanded(
|
// const Expanded(
|
||||||
child: Divider(
|
// child: Divider(
|
||||||
color: ColorsManager.neutralGray,
|
// color: ColorsManager.neutralGray,
|
||||||
thickness: 1.0,
|
// 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: 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),
|
const SizedBox(height: 10),
|
||||||
(tags?.isNotEmpty == true ||
|
DevicesPartWidget(
|
||||||
subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true)
|
tags: tags,
|
||||||
? SizedBox(
|
subspaces: subspaces,
|
||||||
width: screenWidth * 0.25,
|
screenWidth: screenWidth,
|
||||||
child: Container(
|
isTagsAndSubspaceModelDisabled:
|
||||||
padding: const EdgeInsets.all(8.0),
|
isTagsAndSubspaceModelDisabled,
|
||||||
decoration: BoxDecoration(
|
onEditChip: () async {
|
||||||
color: ColorsManager.textFieldGreyColor,
|
await showDialog(
|
||||||
borderRadius: BorderRadius.circular(15),
|
context: context,
|
||||||
border: Border.all(
|
builder: (context) => AssignTagDialog(
|
||||||
color: ColorsManager.textFieldGreyColor,
|
products: widget.products,
|
||||||
width: 3.0, // Border width
|
subspaces: subspaces,
|
||||||
),
|
allTags: widget.allTags,
|
||||||
),
|
addedProducts:
|
||||||
child: Wrap(
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
spacing: 8.0,
|
tags ?? [], subspaces),
|
||||||
runSpacing: 8.0,
|
title: 'Edit Device',
|
||||||
children: [
|
initialTags: TagHelper.generateInitialForTags(
|
||||||
// Combine tags from spaceModel and subspaces
|
spaceTags: tags, subspaces: subspaces),
|
||||||
...TagHelper.groupTags([
|
spaceName: widget.name ?? '',
|
||||||
...?tags,
|
projectTags: widget.projectTags,
|
||||||
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
onSave: (updatedTags, updatedSubspaces) {
|
||||||
]).entries.map(
|
setState(() {
|
||||||
(entry) => Chip(
|
tags = updatedTags;
|
||||||
avatar: SizedBox(
|
subspaces = updatedSubspaces;
|
||||||
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(
|
onTextButtonPressed: () {
|
||||||
icon: Icons.add,
|
isTagsAndSubspaceModelDisabled
|
||||||
label: 'Add Devices',
|
? null
|
||||||
disabled: isTagsAndSubspaceModelDisabled,
|
: _showTagCreateDialog(
|
||||||
))
|
context,
|
||||||
|
enteredName,
|
||||||
|
widget.isEdit,
|
||||||
|
widget.products,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -529,17 +298,32 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
} else if (isNameFieldExist) {
|
} else if (isNameFieldExist) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
String newName = enteredName.isNotEmpty
|
||||||
|
? enteredName
|
||||||
|
: (widget.name ?? '');
|
||||||
if (newName.isNotEmpty) {
|
if (newName.isNotEmpty) {
|
||||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts,
|
if (tags != null && tags!.isNotEmpty) {
|
||||||
selectedSpaceModel, subspaces, tags);
|
if (tags!.any(
|
||||||
|
(tag) => tag.uuid == null || tag.uuid!.isEmpty,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widget.onCreateSpace(
|
||||||
|
newName,
|
||||||
|
selectedIcon,
|
||||||
|
selectedProducts,
|
||||||
|
selectedSpaceModel,
|
||||||
|
subspaces,
|
||||||
|
tags);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
backgroundColor:
|
backgroundColor: isOkButtonEnabled
|
||||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
? ColorsManager.secondaryColor
|
||||||
|
: ColorsManager.grayColor,
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
@ -550,6 +334,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//dialooogggs
|
||||||
void _showIconSelectionDialog() {
|
void _showIconSelectionDialog() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -586,26 +371,50 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
void _showSubSpaceDialog(
|
||||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
BuildContext context,
|
||||||
|
String name,
|
||||||
|
final List<Tag>? spaceTags,
|
||||||
|
bool isEdit,
|
||||||
|
List<ProductModel>? products,
|
||||||
|
final List<SubspaceModel>? existingSubSpaces,
|
||||||
|
) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CreateSubSpaceDialog(
|
return CreateSubSpaceDialog(
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces',
|
||||||
products: products,
|
products: products,
|
||||||
existingSubSpaces: existingSubSpaces,
|
existingSubSpaces: existingSubSpaces,
|
||||||
onSave: (slectedSubspaces) {
|
onSave: (slectedSubspaces, updatedSubSpaces) {
|
||||||
final List<Tag> tagsToAppendToSpace = [];
|
final List<Tag> tagsToAppendToSpace = [];
|
||||||
|
|
||||||
if (slectedSubspaces != null) {
|
if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) {
|
||||||
final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet();
|
final updatedIds =
|
||||||
|
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||||
if (existingSubSpaces != null) {
|
if (existingSubSpaces != null) {
|
||||||
final deletedSubspaces =
|
final deletedSubspaces = existingSubSpaces
|
||||||
existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList();
|
.where((s) => !updatedIds.contains(s.internalId))
|
||||||
|
.toList();
|
||||||
for (var s in deletedSubspaces) {
|
for (var s in deletedSubspaces) {
|
||||||
if (s.tags != null) {
|
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!);
|
tagsToAppendToSpace.addAll(s.tags!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,15 +432,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagCreateDialog(
|
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||||
BuildContext context, String name, bool isEdit, List<ProductModel>? products) {
|
List<ProductModel>? products) {
|
||||||
isEdit
|
isEdit
|
||||||
? showDialog(
|
? showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AssignTagDialog(
|
return AssignTagDialog(
|
||||||
title: 'Edit Device',
|
title: 'Edit Device',
|
||||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
@ -646,7 +456,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +481,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
allTags: widget.allTags,
|
allTags: widget.allTags,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
initialSelectedProducts:
|
initialSelectedProducts:
|
||||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = selectedSpaceTags;
|
tags = selectedSpaceTags;
|
||||||
@ -680,7 +492,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
final int index;
|
final int index;
|
||||||
final String direction;
|
final String direction;
|
||||||
final Offset offset;
|
final Offset offset;
|
||||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
final Function(int index, Offset newPosition) onButtonTap;
|
||||||
|
|
||||||
const PlusButtonWidget({
|
const PlusButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@ -17,35 +17,21 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return GestureDetector(
|
||||||
left: offset.dx,
|
onTap: () {
|
||||||
top: offset.dy,
|
onButtonTap(index, const Offset(0, 150));
|
||||||
child: GestureDetector(
|
},
|
||||||
onTap: () {
|
child: Container(
|
||||||
Offset newPosition;
|
width: 30,
|
||||||
switch (direction) {
|
height: 30,
|
||||||
case 'left':
|
decoration: const BoxDecoration(
|
||||||
newPosition = const Offset(-200, 0);
|
color: ColorsManager.spaceColor,
|
||||||
break;
|
shape: BoxShape.circle,
|
||||||
case 'right':
|
),
|
||||||
newPosition = const Offset(200, 0);
|
child: const Icon(
|
||||||
break;
|
Icons.add,
|
||||||
case 'down':
|
color: ColorsManager.whiteColors,
|
||||||
newPosition = const Offset(0, 150);
|
size: 20,
|
||||||
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 Offset position;
|
||||||
final bool isHovered;
|
final bool isHovered;
|
||||||
final Function(int index, bool isHovered) onHoverChanged;
|
final Function(int index, bool isHovered) onHoverChanged;
|
||||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
final Function(int index, Offset newPosition) onButtonTap;
|
||||||
final Widget Function(int index) buildSpaceContainer;
|
final Widget Function(int index) buildSpaceContainer;
|
||||||
final ValueChanged<Offset> onPositionChanged;
|
final ValueChanged<Offset> onPositionChanged;
|
||||||
|
|
||||||
@ -25,35 +25,34 @@ class SpaceCardWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return MouseRegion(
|
||||||
behavior: HitTestBehavior.opaque,
|
onEnter: (_) => onHoverChanged(index, true),
|
||||||
onPanUpdate: (details) {
|
onExit: (_) => onHoverChanged(index, false),
|
||||||
// Call the provided callback to update the position
|
child: SizedBox(
|
||||||
final newPosition = position + details.delta;
|
width: 140, // Make sure this covers both card and plus button
|
||||||
onPositionChanged(newPosition);
|
height: 90,
|
||||||
},
|
|
||||||
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(
|
child: Stack(
|
||||||
clipBehavior: Clip
|
clipBehavior: Clip.none,
|
||||||
.none, // Allow hovering elements to be displayed outside the boundary
|
|
||||||
children: [
|
children: [
|
||||||
buildSpaceContainer(index), // Build the space container
|
// Main card
|
||||||
if (isHovered) ...[
|
Container(
|
||||||
PlusButtonWidget(
|
width: 140,
|
||||||
index: index,
|
height: 80,
|
||||||
direction: 'down',
|
alignment: Alignment.center,
|
||||||
offset: const Offset(63, 50),
|
color: Colors.transparent,
|
||||||
onButtonTap: onButtonTap,
|
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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -13,7 +13,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final existingTagCounts = <String, int>{};
|
final existingTagCounts = <String, int>{};
|
||||||
for (var tag in initialTags) {
|
for (var tag in initialTags) {
|
||||||
if (tag.product != null) {
|
if (tag.product != null) {
|
||||||
existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
existingTagCounts[tag.product!.uuid] =
|
||||||
|
(existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,14 +23,17 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
for (var selectedProduct in event.addedProducts) {
|
for (var selectedProduct in event.addedProducts) {
|
||||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||||
|
|
||||||
if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) {
|
if (selectedProduct.count == 0 ||
|
||||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
selectedProduct.count <= existingCount) {
|
||||||
|
tags.addAll(initialTags
|
||||||
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final missingCount = selectedProduct.count - existingCount;
|
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) {
|
if (missingCount > 0) {
|
||||||
tags.addAll(List.generate(
|
tags.addAll(List.generate(
|
||||||
@ -85,7 +89,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final tags = List<Tag>.from(currentState.tags);
|
final tags = List<Tag>.from(currentState.tags);
|
||||||
|
|
||||||
// Update the location
|
// 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);
|
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||||
|
|
||||||
@ -117,7 +122,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
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
|
// Recalculate available tags
|
||||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||||
@ -141,8 +147,10 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
|
|
||||||
// Get validation error for duplicate tags
|
// Get validation error for duplicate tags
|
||||||
String? _getValidationError(List<Tag> tags) {
|
String? _getValidationError(List<Tag> tags) {
|
||||||
final nonEmptyTags =
|
final nonEmptyTags = tags
|
||||||
tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList();
|
.map((tag) => tag.tag?.trim() ?? '')
|
||||||
|
.where((tag) => tag.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
final duplicateTags = nonEmptyTags
|
final duplicateTags = nonEmptyTags
|
||||||
.fold<Map<String, int>>({}, (map, tag) {
|
.fold<Map<String, int>>({}, (map, tag) {
|
||||||
@ -168,9 +176,11 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
final availableTags = allTags
|
final availableTags = allTags
|
||||||
.where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
.where((tag) =>
|
||||||
|
tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return availableTags;
|
return projectTags;
|
||||||
|
// availableTags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/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/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
@ -12,9 +7,10 @@ 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_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_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/bloc/assign_tag_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
import 'widgets/save_add_device_row_widget.dart';
|
||||||
|
|
||||||
class AssignTagDialog extends StatelessWidget {
|
class AssignTagDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -29,7 +25,7 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
final List<Tag> projectTags;
|
final List<Tag> projectTags;
|
||||||
|
|
||||||
const AssignTagDialog(
|
const AssignTagDialog(
|
||||||
{Key? key,
|
{super.key,
|
||||||
required this.products,
|
required this.products,
|
||||||
required this.subspaces,
|
required this.subspaces,
|
||||||
required this.addedProducts,
|
required this.addedProducts,
|
||||||
@ -39,13 +35,14 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
required this.spaceName,
|
required this.spaceName,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.onSave,
|
this.onSave,
|
||||||
required this.projectTags})
|
required this.projectTags});
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<String> locations =
|
final List<String> locations = (subspaces ?? [])
|
||||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
.map((subspace) => subspace.subspaceName)
|
||||||
|
.toList()
|
||||||
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagBloc(projectTags)
|
create: (_) => AssignTagBloc(projectTags)
|
||||||
@ -67,131 +64,31 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
AssignTagsTable(
|
||||||
borderRadius: BorderRadius.circular(20),
|
controllers: controllers,
|
||||||
child: DataTable(
|
locations: locations,
|
||||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
tags: state.tags,
|
||||||
key: ValueKey(state.tags.length),
|
updatedTags: state.updatedTags,
|
||||||
border: TableBorder.all(
|
onDeleteDevice: ({required index, required tag}) {
|
||||||
color: ColorsManager.dataHeaderGrey,
|
context
|
||||||
width: 1,
|
.read<AssignTagBloc>()
|
||||||
borderRadius: BorderRadius.circular(20),
|
.add(DeleteTag(tagToDelete: tag, tags: state.tags));
|
||||||
),
|
|
||||||
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];
|
|
||||||
|
|
||||||
return DataRow(
|
controllers.removeAt(index);
|
||||||
cells: [
|
},
|
||||||
DataCell(Text((index + 1).toString())),
|
onTagDropDownSelected: ({required index, required tag}) {
|
||||||
DataCell(
|
context.read<AssignTagBloc>().add(UpdateTagEvent(
|
||||||
Row(
|
index: index,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
tag: tag,
|
||||||
children: [
|
));
|
||||||
Expanded(
|
},
|
||||||
child: Text(
|
onLocationDropDownSelected: (
|
||||||
tag.product?.name ?? 'Unknown',
|
{required index, required location}) {
|
||||||
overflow: TextOverflow.ellipsis,
|
context.read<AssignTagBloc>().add(UpdateLocation(
|
||||||
)),
|
index: index,
|
||||||
const SizedBox(width: 10),
|
location: location,
|
||||||
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)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(state.errorMessage!,
|
||||||
@ -203,69 +100,15 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
SaveAddDeviceRowWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
subspaces: subspaces,
|
||||||
children: [
|
products: products,
|
||||||
const SizedBox(width: 10),
|
projectTags: projectTags,
|
||||||
Expanded(
|
spaceName: spaceName,
|
||||||
child: Builder(
|
onSave: onSave,
|
||||||
builder: (buttonContext) => CancelButton(
|
allTags: allTags,
|
||||||
label: 'Add New Device',
|
tags: state.tags,
|
||||||
onPressed: () async {
|
isSaveEnabled: state.isSaveEnabled,
|
||||||
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),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
import '../../../add_device_type/views/add_device_type_widget.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
|
||||||
|
class SaveAddDeviceRowWidget extends StatelessWidget {
|
||||||
|
const SaveAddDeviceRowWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.products,
|
||||||
|
required this.projectTags,
|
||||||
|
required this.spaceName,
|
||||||
|
required this.onSave,
|
||||||
|
required this.allTags,
|
||||||
|
required this.tags,
|
||||||
|
required this.isSaveEnabled,
|
||||||
|
});
|
||||||
|
final List<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,9 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/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/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
@ -12,11 +8,10 @@ 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/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/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/models/subspace_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'widgets/RowOfCancelSaveWidget.dart';
|
||||||
|
import 'widgets/assign_tags_tables_widget.dart';
|
||||||
|
|
||||||
class AssignTagModelsDialog extends StatelessWidget {
|
class AssignTagModelsDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -53,8 +48,10 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<String> locations =
|
final List<String> locations = (subspaces ?? [])
|
||||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
.map((subspace) => subspace.subspaceName)
|
||||||
|
.toList()
|
||||||
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagModelBloc(projectTags)
|
create: (_) => AssignTagModelBloc(projectTags)
|
||||||
@ -78,137 +75,38 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
AssignTagsTable(
|
||||||
borderRadius: BorderRadius.circular(20),
|
controllers: controllers,
|
||||||
child: DataTable(
|
locations: locations,
|
||||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
tags: state.tags,
|
||||||
key: ValueKey(state.tags.length),
|
updatedTags: state.updatedTags,
|
||||||
border: TableBorder.all(
|
onDeleteDevice: ({required index, required tag}) {
|
||||||
color: ColorsManager.dataHeaderGrey,
|
context
|
||||||
width: 1,
|
.read<AssignTagModelBloc>()
|
||||||
borderRadius: BorderRadius.circular(20),
|
.add(DeleteTagModel(
|
||||||
),
|
tagToDelete: tag,
|
||||||
columns: [
|
tags: state.tags,
|
||||||
DataColumn(
|
));
|
||||||
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
controllers.removeAt(index);
|
||||||
DataColumn(
|
},
|
||||||
label: Text('Device',
|
onTagDropDownSelected: (
|
||||||
style: Theme.of(context).textTheme.bodyMedium)),
|
{required index, required tag}) {
|
||||||
DataColumn(
|
context.read<AssignTagModelBloc>().add(
|
||||||
numeric: false,
|
UpdateTag(
|
||||||
label:
|
index: index,
|
||||||
Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
tag: tag,
|
||||||
DataColumn(
|
),
|
||||||
label: Text('Location',
|
);
|
||||||
style: Theme.of(context).textTheme.bodyMedium)),
|
},
|
||||||
],
|
onLocationDropDownSelected: (
|
||||||
rows: state.tags.isEmpty
|
{required index, required location}) {
|
||||||
? [
|
context.read<AssignTagModelBloc>().add(
|
||||||
DataRow(cells: [
|
UpdateLocation(
|
||||||
DataCell(
|
index: index,
|
||||||
Center(
|
location: location,
|
||||||
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)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(state.errorMessage!,
|
||||||
@ -220,101 +118,16 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
RowOfSaveCancelWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
subspaces: subspaces,
|
||||||
children: [
|
products: products,
|
||||||
const SizedBox(width: 10),
|
allTags: allTags,
|
||||||
Expanded(
|
spaceName: spaceName,
|
||||||
child: Builder(
|
otherSpaceModels: otherSpaceModels,
|
||||||
builder: (buttonContext) => CancelButton(
|
pageContext: pageContext,
|
||||||
label: 'Add New Device',
|
projectTags: projectTags,
|
||||||
onPressed: () async {
|
spaceModel: spaceModel,
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
allSpaceModels: allSpaceModels,
|
||||||
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),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,152 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../common/buttons/cancel_button.dart';
|
||||||
|
import '../../../../common/buttons/default_button.dart';
|
||||||
|
import '../../../all_spaces/model/product_model.dart';
|
||||||
|
import '../../../all_spaces/model/tag.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
import '../../../space_model/models/space_template_model.dart';
|
||||||
|
import '../../../space_model/models/subspace_template_model.dart';
|
||||||
|
import '../../../space_model/widgets/dialog/create_space_model_dialog.dart';
|
||||||
|
import '../../../tag_model/views/add_device_type_model_widget.dart';
|
||||||
|
import '../../bloc/assign_tag_model_bloc.dart';
|
||||||
|
import '../../bloc/assign_tag_model_state.dart';
|
||||||
|
|
||||||
|
class RowOfSaveCancelWidget extends StatelessWidget {
|
||||||
|
const RowOfSaveCancelWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.products,
|
||||||
|
required this.allTags,
|
||||||
|
required this.spaceName,
|
||||||
|
required this.otherSpaceModels,
|
||||||
|
required this.pageContext,
|
||||||
|
required this.projectTags,
|
||||||
|
required this.spaceModel,
|
||||||
|
required this.allSpaceModels,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
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(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
updatedSubspaceModels,
|
updatedSubspaceModels,
|
||||||
errorMessage,
|
errorMessage ,
|
||||||
updatedDuplicates,
|
updatedDuplicates,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/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/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/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_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_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.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 {
|
class CreateSubSpaceDialog extends StatefulWidget {
|
||||||
final String dialogTitle;
|
final String dialogTitle;
|
||||||
final List<SubspaceModel>? existingSubSpaces;
|
final List<SubspaceModel>? existingSubSpaces;
|
||||||
final String? spaceName;
|
final String? spaceName;
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final void Function(List<SubspaceModel>?)? onSave;
|
final void Function(
|
||||||
|
List<SubspaceModel>?, List<UpdateSubspaceModel> updatedSubSpaces)? onSave;
|
||||||
|
|
||||||
const CreateSubSpaceDialog({
|
const CreateSubSpaceDialog({
|
||||||
required this.dialogTitle,
|
required this.dialogTitle,
|
||||||
@ -33,14 +33,7 @@ class CreateSubSpaceDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||||
late final TextEditingController _subspaceNameController;
|
final TextEditingController _subspaceNameController = TextEditingController();
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_subspaceNameController = TextEditingController();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subspaceNameController.dispose();
|
_subspaceNameController.dispose();
|
||||||
@ -79,83 +72,26 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
color: ColorsManager.blackColor,
|
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),
|
const SizedBox(height: 16),
|
||||||
Container(
|
TextFieldSubSpaceDialogWidget(
|
||||||
width: context.screenWidth * 0.35,
|
subspaceNameController: _subspaceNameController,
|
||||||
padding: const EdgeInsets.symmetric(
|
subSpaces: state.subSpaces,
|
||||||
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)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
@ -168,36 +104,10 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
OkCancelSubSpaceWidget(
|
||||||
children: [
|
subspaceNameController: _subspaceNameController,
|
||||||
Expanded(
|
errorMessage: state.errorMessage,
|
||||||
child: CancelButton(
|
onSave: widget.onSave,
|
||||||
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'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
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'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../all_spaces/model/subspace_model.dart';
|
||||||
|
import '../../../create_subspace_model/widgets/subspace_chip.dart';
|
||||||
|
import '../../../space_model/models/subspace_template_model.dart';
|
||||||
|
import '../../bloc/subspace_bloc.dart';
|
||||||
|
import '../../bloc/subspace_event.dart';
|
||||||
|
|
||||||
|
class TextFieldSubSpaceDialogWidget extends StatelessWidget {
|
||||||
|
const TextFieldSubSpaceDialogWidget({
|
||||||
|
super.key,
|
||||||
|
required TextEditingController subspaceNameController,
|
||||||
|
required this.subSpaces,
|
||||||
|
}) : _subspaceNameController = subspaceNameController;
|
||||||
|
|
||||||
|
final TextEditingController _subspaceNameController;
|
||||||
|
final List<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) {
|
if (state is InitialState) {
|
||||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
||||||
}
|
}
|
||||||
if (state is CommunityStructureState) {
|
if (state is CommunityStructureState) {
|
||||||
context.read<SpaceManagementBloc>().add(BlankStateEvent(context));
|
context.read<SpaceManagementBloc>().add(BlankStateEvent(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state is SpaceModelState) {
|
if (state is SpaceModelState) {
|
||||||
context.read<SpaceManagementBloc>().add(SpaceModelLoadEvent(context));
|
context.read<SpaceManagementBloc>().add(SpaceModelLoadEvent(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,15 +31,19 @@ class CenterBodyWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
context
|
||||||
|
.read<CenterBodyBloc>()
|
||||||
|
.add(CommunityStructureSelectedEvent());
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Community Structure',
|
'Community Structure',
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
fontWeight: state is CommunityStructureState || state is CommunitySelectedState
|
fontWeight: state is CommunityStructureState ||
|
||||||
|
state is CommunitySelectedState
|
||||||
? FontWeight.bold
|
? FontWeight.bold
|
||||||
: FontWeight.normal,
|
: 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.bodyLarge!.color
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -50,26 +54,26 @@ class CenterBodyWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
GestureDetector(
|
// GestureDetector(
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
context.read<CenterBodyBloc>().add(SpaceModelSelectedEvent());
|
// context.read<CenterBodyBloc>().add(SpaceModelSelectedEvent());
|
||||||
},
|
// },
|
||||||
child: Text(
|
// child: Text(
|
||||||
'Space Model',
|
// 'Space Model',
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
// style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
fontWeight: state is SpaceModelState
|
// fontWeight: state is SpaceModelState
|
||||||
? FontWeight.bold
|
// ? FontWeight.bold
|
||||||
: FontWeight.normal,
|
// : FontWeight.normal,
|
||||||
color: state is SpaceModelState
|
// color: state is SpaceModelState
|
||||||
? Theme.of(context).textTheme.bodyLarge!.color
|
// ? Theme.of(context).textTheme.bodyLarge!.color
|
||||||
: Theme.of(context)
|
// : Theme.of(context)
|
||||||
.textTheme
|
// .textTheme
|
||||||
.bodyLarge!
|
// .bodyLarge!
|
||||||
.color!
|
// .color!
|
||||||
.withOpacity(0.5),
|
// .withOpacity(0.5),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -24,37 +24,22 @@ class AddDeviceTypeModelBloc
|
|||||||
|
|
||||||
if (currentState is AddDeviceModelLoaded) {
|
if (currentState is AddDeviceModelLoaded) {
|
||||||
final existingProduct = currentState.selectedProducts.firstWhere(
|
final existingProduct = currentState.selectedProducts.firstWhere(
|
||||||
(p) => p.productId == event.productId,
|
(p) => p.productId == event.selectedProduct.productId,
|
||||||
orElse: () => SelectedProduct(
|
orElse: () => event.selectedProduct,
|
||||||
productId: event.productId,
|
|
||||||
count: 0,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
List<SelectedProduct> updatedProducts;
|
List<SelectedProduct> updatedProducts;
|
||||||
|
|
||||||
if (event.count > 0) {
|
if (event.selectedProduct.count > 0) {
|
||||||
if (!currentState.selectedProducts.contains(existingProduct)) {
|
if (!currentState.selectedProducts.contains(existingProduct)) {
|
||||||
updatedProducts = [
|
updatedProducts = [
|
||||||
...currentState.selectedProducts,
|
...currentState.selectedProducts,
|
||||||
SelectedProduct(
|
event.selectedProduct,
|
||||||
productId: event.productId,
|
|
||||||
count: event.count,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
updatedProducts = currentState.selectedProducts.map((p) {
|
updatedProducts = currentState.selectedProducts.map((p) {
|
||||||
if (p.productId == event.productId) {
|
if (p.productId == event.selectedProduct.productId) {
|
||||||
return SelectedProduct(
|
return event.selectedProduct;
|
||||||
productId: p.productId,
|
|
||||||
count: event.count,
|
|
||||||
productName: p.productName,
|
|
||||||
product: p.product,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}).toList();
|
}).toList();
|
||||||
@ -62,7 +47,7 @@ class AddDeviceTypeModelBloc
|
|||||||
} else {
|
} else {
|
||||||
// Remove the product if the count is 0
|
// Remove the product if the count is 0
|
||||||
updatedProducts = currentState.selectedProducts
|
updatedProducts = currentState.selectedProducts
|
||||||
.where((p) => p.productId != event.productId)
|
.where((p) => p.productId != event.selectedProduct.productId)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,20 +10,15 @@ abstract class AddDeviceTypeModelEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
||||||
final String productId;
|
final SelectedProduct selectedProduct;
|
||||||
final int count;
|
|
||||||
final String productName;
|
|
||||||
final ProductModel product;
|
|
||||||
|
|
||||||
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
UpdateProductCountEvent({required this.selectedProduct});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [productId, count];
|
List<Object> get props => [selectedProduct];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
||||||
final List<Tag> initialTags;
|
final List<Tag> initialTags;
|
||||||
final List<SelectedProduct> addedProducts;
|
final List<SelectedProduct> addedProducts;
|
||||||
|
@ -54,10 +54,10 @@ class DeviceTypeTileWidget extends StatelessWidget {
|
|||||||
onCountChanged: (newCount) {
|
onCountChanged: (newCount) {
|
||||||
context.read<AddDeviceTypeModelBloc>().add(
|
context.read<AddDeviceTypeModelBloc>().add(
|
||||||
UpdateProductCountEvent(
|
UpdateProductCountEvent(
|
||||||
productId: product.uuid,
|
selectedProduct: product.toSelectedProduct(
|
||||||
count: newCount,
|
newCount,
|
||||||
productName: product.catName,
|
),
|
||||||
product: product),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -22,6 +22,18 @@ class HTTPService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
client.interceptors.add(serviceLocator.get<HTTPInterceptor>());
|
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;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,22 +4,27 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_m
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/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_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/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/space_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
|
import '../pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
|
|
||||||
class CommunitySpaceManagementApi {
|
class CommunitySpaceManagementApi {
|
||||||
// Community Management APIs
|
// Community Management APIs
|
||||||
Future<List<CommunityModel>> fetchCommunities(String projectId, {int page = 1}) async {
|
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
||||||
|
{int page = 1}) async {
|
||||||
try {
|
try {
|
||||||
List<CommunityModel> allCommunities = [];
|
List<CommunityModel> allCommunities = [];
|
||||||
bool hasNext = true;
|
bool hasNext = true;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
await HTTPService().get(
|
await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
path: ApiEndpoints.getCommunityList
|
||||||
|
.replaceAll('{projectId}', projectId),
|
||||||
queryParameters: {
|
queryParameters: {
|
||||||
'page': page,
|
'page': page,
|
||||||
},
|
},
|
||||||
@ -55,8 +60,14 @@ class CommunitySpaceManagementApi {
|
|||||||
try {
|
try {
|
||||||
bool hasNext = false;
|
bool hasNext = false;
|
||||||
await HTTPService().get(
|
await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
path:
|
||||||
queryParameters: {'page': page, 'includeSpaces': true, 'size': 25, 'search': search},
|
ApiEndpoints.getCommunityList.replaceAll('{projectId}', projectId),
|
||||||
|
queryParameters: {
|
||||||
|
'page': page,
|
||||||
|
'includeSpaces': true,
|
||||||
|
'size': 25,
|
||||||
|
'search': search
|
||||||
|
},
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
try {
|
try {
|
||||||
List<dynamic> jsonData = json['data'] ?? [];
|
List<dynamic> jsonData = json['data'] ?? [];
|
||||||
@ -68,7 +79,10 @@ class CommunitySpaceManagementApi {
|
|||||||
|
|
||||||
page = currentPage + 1;
|
page = currentPage + 1;
|
||||||
paginationModel = PaginationModel(
|
paginationModel = PaginationModel(
|
||||||
pageNum: page, hasNext: hasNext, size: 25, communities: communityList);
|
pageNum: page,
|
||||||
|
hasNext: hasNext,
|
||||||
|
size: 25,
|
||||||
|
communities: communityList);
|
||||||
return paginationModel;
|
return paginationModel;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
hasNext = false;
|
hasNext = false;
|
||||||
@ -83,7 +97,8 @@ class CommunitySpaceManagementApi {
|
|||||||
Future<CommunityModel?> getCommunityById(String communityId) async {
|
Future<CommunityModel?> getCommunityById(String communityId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getCommunityById.replaceAll('{communityId}', communityId),
|
path: ApiEndpoints.getCommunityById
|
||||||
|
.replaceAll('{communityId}', communityId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return CommunityModel.fromJson(json['data']);
|
return CommunityModel.fromJson(json['data']);
|
||||||
},
|
},
|
||||||
@ -95,7 +110,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CommunityModel?> createCommunity(String name, String description, String projectId) async {
|
Future<CommunityModel?> createCommunity(
|
||||||
|
String name, String description, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().post(
|
final response = await HTTPService().post(
|
||||||
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
path: ApiEndpoints.createCommunity.replaceAll('{projectId}', projectId),
|
||||||
@ -114,7 +130,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateCommunity(String communityId, String name, String projectId) async {
|
Future<bool> updateCommunity(
|
||||||
|
String communityId, String name, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.updateCommunity
|
path: ApiEndpoints.updateCommunity
|
||||||
@ -151,7 +168,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpacesResponse> fetchSpaces(String communityId, String projectId) async {
|
Future<SpacesResponse> fetchSpaces(
|
||||||
|
String communityId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.listSpaces
|
path: ApiEndpoints.listSpaces
|
||||||
@ -177,7 +195,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceModel?> getSpace(String communityId, String spaceId, String projectId) async {
|
Future<SpaceModel?> getSpace(
|
||||||
|
String communityId, String spaceId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpace
|
path: ApiEndpoints.getSpace
|
||||||
@ -199,7 +218,6 @@ class CommunitySpaceManagementApi {
|
|||||||
{required String communityId,
|
{required String communityId,
|
||||||
required String name,
|
required String name,
|
||||||
String? parentId,
|
String? parentId,
|
||||||
String? direction,
|
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
String? spaceModelUuid,
|
String? spaceModelUuid,
|
||||||
@ -213,7 +231,6 @@ class CommunitySpaceManagementApi {
|
|||||||
'isPrivate': isPrivate,
|
'isPrivate': isPrivate,
|
||||||
'x': position.dx,
|
'x': position.dx,
|
||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
};
|
};
|
||||||
if (parentId != null) {
|
if (parentId != null) {
|
||||||
@ -248,11 +265,10 @@ class CommunitySpaceManagementApi {
|
|||||||
required String name,
|
required String name,
|
||||||
String? parentId,
|
String? parentId,
|
||||||
String? icon,
|
String? icon,
|
||||||
String? direction,
|
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
List<TagModelUpdate>? tags,
|
List<Tag>? tags,
|
||||||
List<UpdateSubspaceTemplateModel>? subspaces,
|
List<SubspaceModel>? subspaces,
|
||||||
String? spaceModelUuid,
|
String? spaceModelUuid,
|
||||||
required String projectId}) async {
|
required String projectId}) async {
|
||||||
try {
|
try {
|
||||||
@ -261,9 +277,8 @@ class CommunitySpaceManagementApi {
|
|||||||
'isPrivate': isPrivate,
|
'isPrivate': isPrivate,
|
||||||
'x': position.dx,
|
'x': position.dx,
|
||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
'subspace': subspaces,
|
'subspaces': subspaces,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
'spaceModelUuid': spaceModelUuid,
|
'spaceModelUuid': spaceModelUuid,
|
||||||
};
|
};
|
||||||
@ -289,7 +304,8 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> deleteSpace(String communityId, String spaceId, String projectId) async {
|
Future<bool> deleteSpace(
|
||||||
|
String communityId, String spaceId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().delete(
|
final response = await HTTPService().delete(
|
||||||
path: ApiEndpoints.deleteSpace
|
path: ApiEndpoints.deleteSpace
|
||||||
@ -307,15 +323,17 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> getSpaceHierarchy(String communityId, String projectId) async {
|
Future<List<SpaceModel>> getSpaceHierarchy(
|
||||||
|
String communityId, String projectId) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpaceHierarchy
|
path: ApiEndpoints.getSpaceHierarchy
|
||||||
.replaceAll('{communityId}', communityId)
|
.replaceAll('{communityId}', communityId)
|
||||||
.replaceAll('{projectId}', projectId),
|
.replaceAll('{projectId}', projectId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final spaceModels =
|
final spaceModels = (json['data'] as List)
|
||||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
|
.toList();
|
||||||
|
|
||||||
return spaceModels;
|
return spaceModels;
|
||||||
},
|
},
|
||||||
@ -327,15 +345,17 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> getSpaceOnlyWithDevices({String? communityId, String? projectId}) async {
|
Future<List<SpaceModel>> getSpaceOnlyWithDevices(
|
||||||
|
{String? communityId, String? projectId}) async {
|
||||||
try {
|
try {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.spaceOnlyWithDevices
|
path: ApiEndpoints.spaceOnlyWithDevices
|
||||||
.replaceAll('{communityId}', communityId!)
|
.replaceAll('{communityId}', communityId!)
|
||||||
.replaceAll('{projectId}', projectId!),
|
.replaceAll('{projectId}', projectId!),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
final spaceModels =
|
final spaceModels = (json['data'] as List)
|
||||||
(json['data'] as List).map((spaceJson) => SpaceModel.fromJson(spaceJson)).toList();
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
|
.toList();
|
||||||
return spaceModels;
|
return spaceModels;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/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/create_space_template_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
@ -7,17 +9,23 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
|||||||
class SpaceModelManagementApi {
|
class SpaceModelManagementApi {
|
||||||
Future<List<SpaceTemplateModel>> listSpaceModels(
|
Future<List<SpaceTemplateModel>> listSpaceModels(
|
||||||
{required String projectId, int page = 1}) async {
|
{required String projectId, int page = 1}) async {
|
||||||
final response = await HTTPService().get(
|
try {
|
||||||
path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
// final response = await HTTPService().get(
|
||||||
queryParameters: {'page': page},
|
// path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
||||||
expectedResponseModel: (json) {
|
// queryParameters: {'page': page},
|
||||||
List<dynamic> jsonData = json['data'];
|
// expectedResponseModel: (json) {
|
||||||
return jsonData.map((jsonItem) {
|
// List<dynamic> jsonData = json['data'];
|
||||||
return SpaceTemplateModel.fromJson(jsonItem);
|
// return jsonData.map((jsonItem) {
|
||||||
}).toList();
|
// return SpaceTemplateModel.fromJson(jsonItem);
|
||||||
},
|
// }).toList();
|
||||||
);
|
// },
|
||||||
return response;
|
// );
|
||||||
|
return [];
|
||||||
|
// response;
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> createSpaceModel(
|
Future<SpaceTemplateModel?> createSpaceModel(
|
||||||
@ -33,8 +41,8 @@ class SpaceModelManagementApi {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> updateSpaceModel(
|
Future<String?> updateSpaceModel(CreateSpaceTemplateBodyModel spaceModel,
|
||||||
CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid, String projectId) async {
|
String spaceModelUuid, String projectId) async {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.updateSpaceModel
|
path: ApiEndpoints.updateSpaceModel
|
||||||
.replaceAll('{projectId}', projectId)
|
.replaceAll('{projectId}', projectId)
|
||||||
@ -47,7 +55,8 @@ class SpaceModelManagementApi {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> getSpaceModel(String spaceModelUuid, String projectId) async {
|
Future<SpaceTemplateModel?> getSpaceModel(
|
||||||
|
String spaceModelUuid, String projectId) async {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpaceModel
|
path: ApiEndpoints.getSpaceModel
|
||||||
.replaceAll('{projectId}', projectId)
|
.replaceAll('{projectId}', projectId)
|
||||||
|
@ -2,6 +2,7 @@ class Assets {
|
|||||||
Assets._();
|
Assets._();
|
||||||
static const String background = "assets/images/Background.png";
|
static const String background = "assets/images/Background.png";
|
||||||
static const String webBackground = "assets/images/web_Background.svg";
|
static const String webBackground = "assets/images/web_Background.svg";
|
||||||
|
static const String webBackgroundPng = "assets/images/web_Background.png";
|
||||||
static const String blackLogo = "assets/images/black-logo.png";
|
static const String blackLogo = "assets/images/black-logo.png";
|
||||||
static const String logo = "assets/images/Logo.svg";
|
static const String logo = "assets/images/Logo.svg";
|
||||||
static const String logoHorizontal = "assets/images/logo_horizontal.png";
|
static const String logoHorizontal = "assets/images/logo_horizontal.png";
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user