mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-11 15:47:44 +00:00
Compare commits
5 Commits
bugfix/fix
...
chore/remo
Author | SHA1 | Date | |
---|---|---|---|
c5871be990 | |||
97bdb1bbb7 | |||
7ce0a27af0 | |||
bc4af6a237 | |||
513175ed1e |
@ -1,16 +0,0 @@
|
|||||||
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M17.9807 2.67199L16.6413 0.219727H7.91051C7.43122 0.219727 7.04266 0.608281 7.04266 1.08758V16.605C7.04266 17.0843 7.43122 17.4729 7.91051 17.4729H18.2257C18.705 17.4729 19.0935 17.0843 19.0935 16.605V16.5459H19.7312V3.8127L17.9807 2.67199Z" fill="#60B7FF"/>
|
|
||||||
<path d="M18.1513 6.23445H9.12553C8.95881 6.23445 8.82373 6.09934 8.82373 5.93266C8.82373 5.76598 8.95885 5.63086 9.12553 5.63086H18.1513C18.318 5.63086 18.4531 5.76598 18.4531 5.93266C18.4531 6.09934 18.318 6.23445 18.1513 6.23445Z" fill="#0055A3"/>
|
|
||||||
<path d="M18.1513 8.54891H9.12553C8.95881 8.54891 8.82373 8.41379 8.82373 8.24711C8.82373 8.08043 8.95885 7.94531 9.12553 7.94531H18.1513C18.318 7.94531 18.4531 8.08043 18.4531 8.24711C18.4531 8.41379 18.318 8.54891 18.1513 8.54891Z" fill="#0055A3"/>
|
|
||||||
<path d="M18.1513 10.8634H9.12553C8.95881 10.8634 8.82373 10.7282 8.82373 10.5616C8.82373 10.3949 8.95885 10.2598 9.12553 10.2598H18.1513C18.318 10.2598 18.4531 10.3949 18.4531 10.5616C18.4531 10.7282 18.318 10.8634 18.1513 10.8634Z" fill="#0055A3"/>
|
|
||||||
<path d="M18.1513 13.1778H9.12556C8.95884 13.1778 8.82376 13.0427 8.82376 12.876C8.82376 12.7093 8.95888 12.5742 9.12556 12.5742H18.1513C18.3181 12.5742 18.4531 12.7093 18.4531 12.876C18.4531 13.0427 18.3181 13.1778 18.1513 13.1778Z" fill="#0055A3"/>
|
|
||||||
<path d="M19.0935 3.39648V16.6044C19.0935 17.0837 18.7049 17.4722 18.2256 17.4722H19.3663C19.8456 17.4722 20.2342 17.0837 20.2342 16.6044V3.81203L19.0935 3.39648Z" fill="#26A6FE"/>
|
|
||||||
<path d="M17.5091 3.8127H20.2342L16.6413 0.219727V2.94484C16.6413 3.42414 17.0298 3.8127 17.5091 3.8127Z" fill="#004281"/>
|
|
||||||
<path d="M11.4172 19.7805C11.8965 19.7805 12.2851 19.392 12.2851 18.9127V18.8937H12.8297V6.12031L11.039 4.78906L9.83279 2.52734H1.10204C0.622747 2.52734 0.234192 2.9159 0.234192 3.3952V18.9127C0.234192 19.392 0.622747 19.7805 1.10204 19.7805H11.4172Z" fill="#D5EDFF"/>
|
|
||||||
<path d="M12.285 4.97852V6.11922V18.9116C12.285 19.3909 11.8964 19.7794 11.4171 19.7794H12.5578C13.0371 19.7794 13.4257 19.3909 13.4257 18.9116V6.11922L12.285 4.97852Z" fill="#D8ECFE"/>
|
|
||||||
<path d="M10.7006 6.12031H13.4258L9.83279 2.52734V5.25246C9.83276 5.73176 10.2213 6.12031 10.7006 6.12031Z" fill="#B3DAFE"/>
|
|
||||||
<path d="M11.3429 8.54891H2.31709C2.15037 8.54891 2.01529 8.41379 2.01529 8.24711C2.01529 8.08043 2.15041 7.94531 2.31709 7.94531H11.3429C11.5096 7.94531 11.6447 8.08043 11.6447 8.24711C11.6447 8.41379 11.5096 8.54891 11.3429 8.54891Z" fill="#82AEE3"/>
|
|
||||||
<path d="M11.3428 10.8634H2.31706C2.15034 10.8634 2.01526 10.7282 2.01526 10.5616C2.01526 10.3949 2.15038 10.2598 2.31706 10.2598H11.3428C11.5096 10.2598 11.6446 10.3949 11.6446 10.5616C11.6446 10.7282 11.5096 10.8634 11.3428 10.8634Z" fill="#82AEE3"/>
|
|
||||||
<path d="M11.3428 13.1778H2.31706C2.15034 13.1778 2.01526 13.0427 2.01526 12.876C2.01526 12.7093 2.15038 12.5742 2.31706 12.5742H11.3428C11.5096 12.5742 11.6446 12.7093 11.6446 12.876C11.6446 13.0427 11.5096 13.1778 11.3428 13.1778Z" fill="#82AEE3"/>
|
|
||||||
<path d="M11.3428 15.4923H2.31706C2.15034 15.4923 2.01526 15.3571 2.01526 15.1905C2.01526 15.0238 2.15038 14.8887 2.31706 14.8887H11.3428C11.5096 14.8887 11.6446 15.0238 11.6446 15.1905C11.6447 15.3571 11.5096 15.4923 11.3428 15.4923Z" fill="#82AEE3"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.2 KiB |
@ -1,22 +0,0 @@
|
|||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g filter="url(#filter0_d_4618_3290)">
|
|
||||||
<path d="M18.7967 7.35156V19.8515C18.7967 21.0362 17.8329 22 16.6482 22H4.14825C2.9636 22 1.99982 21.0362 1.99982 19.8515V7.35156C1.99982 6.16691 2.9636 5.20312 4.14825 5.20312H16.6482C17.8329 5.20312 18.7967 6.16691 18.7967 7.35156Z" fill="#EDF2F2"/>
|
|
||||||
<path d="M18.7967 19.8515C18.7967 21.0361 17.8329 21.9999 16.6482 21.9999H4.14825C3.55591 21.9999 3.0188 21.7589 2.62978 21.3699L18.1667 5.83301C18.5557 6.22203 18.7967 6.75914 18.7967 7.35148V19.8515Z" fill="#C9DCDC"/>
|
|
||||||
<path d="M9.28417 14.7153C9.12722 14.5583 9.07241 14.3262 9.14265 14.1157L9.97128 11.6297C10 11.5434 10.0485 11.465 10.1128 11.4007L17.8468 3.66674C18.0756 3.43791 18.4466 3.43791 18.6754 3.66674L20.3327 5.324C20.5615 5.55283 20.5615 5.9238 20.3327 6.15263L12.5987 13.8866C12.5344 13.9509 12.456 13.9994 12.3697 14.0281L9.88374 14.8567C9.67323 14.927 9.44109 14.8722 9.28417 14.7153Z" fill="#4998EE"/>
|
|
||||||
<path d="M19.504 4.49512L9.28413 14.715C9.44105 14.8719 9.6732 14.9267 9.88374 14.8565L12.3697 14.0279C12.456 13.9992 12.5344 13.9507 12.5987 13.8864L20.3327 6.15242C20.5615 5.92359 20.5615 5.55261 20.3327 5.32379L19.504 4.49512Z" fill="#176EDE"/>
|
|
||||||
<path d="M20.3327 6.15305L17.8467 3.66711L19.2278 2.28602C19.6092 1.90466 20.2275 1.90466 20.6089 2.28602L21.7137 3.39087C22.0951 3.77223 22.0951 4.39055 21.7137 4.77192L20.3327 6.15305Z" fill="#FFE137"/>
|
|
||||||
<path d="M21.1613 2.83789L19.0897 4.90949L20.3327 6.15245L21.7138 4.77136C22.0951 4.39 22.0951 3.77168 21.7138 3.39031L21.1613 2.83789Z" fill="#FAC814"/>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_d_4618_3290" x="-0.000183105" y="0" width="24" height="24" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
||||||
<feOffset/>
|
|
||||||
<feGaussianBlur stdDeviation="1"/>
|
|
||||||
<feComposite in2="hardAlpha" operator="out"/>
|
|
||||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
||||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_4618_3290"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_4618_3290" result="shape"/>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.3 KiB |
@ -1,9 +0,0 @@
|
|||||||
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M11.8069 19.9998H3.50035C2.16411 19.9998 1.08087 18.9165 1.08087 17.5804V4.51562H14.2262V17.5804C14.2262 18.9165 13.1432 19.9998 11.8069 19.9998Z" fill="#D8D8D8"/>
|
|
||||||
<path d="M1.08087 4.51562V5.48335H12.3715C12.8168 5.48335 13.1779 5.84438 13.1779 6.2898V17.3384C13.1779 18.2292 12.4557 18.9513 11.5649 18.9513H2.4519C2.05409 18.9513 1.67887 18.8547 1.34775 18.6844C1.74907 19.4652 2.56207 19.9998 3.50035 19.9998H11.8069C13.1432 19.9998 14.2262 18.9165 14.2262 17.5803V4.51562H1.08087Z" fill="#BABABA"/>
|
|
||||||
<path d="M14.2667 1.77417H10.7439L10.1633 0.612956C9.9742 0.234837 9.5941 0 9.17142 0H6.13594C5.71311 0 5.33316 0.234837 5.1441 0.612956L4.56349 1.77417H1.04063C0.595221 1.77417 0.234192 2.1352 0.234192 2.58061V3.70978C0.234192 4.15504 0.595221 4.51622 1.04063 4.51622H14.2667C14.7121 4.51622 15.0732 4.15504 15.0732 3.70978V2.58077C15.0732 2.1352 14.7121 1.77417 14.2667 1.77417ZM5.68503 0.8835C5.77094 0.71153 5.94368 0.604869 6.13594 0.604869H9.17142C9.36354 0.604869 9.53627 0.71153 9.62218 0.8835L10.0676 1.77417H5.23977L5.68503 0.8835Z" fill="#757575"/>
|
|
||||||
<path d="M14.2668 1.77441H12.9763C13.4217 1.77441 13.7829 2.13544 13.7829 2.58086V3.71003C13.7829 4.15529 13.4217 4.51647 12.9763 4.51647H14.2668C14.7122 4.51647 15.0732 4.15529 15.0732 3.71003V2.58101C15.0732 2.13544 14.7122 1.77441 14.2668 1.77441Z" fill="#595959"/>
|
|
||||||
<path d="M11.3634 17.5C10.918 17.5 10.5569 17.139 10.5569 16.6935V9.15312C10.5569 8.70771 10.918 8.34668 11.3634 8.34668C11.8088 8.34668 12.1698 8.70771 12.1698 9.15312V16.6935C12.1698 17.139 11.8088 17.5 11.3634 17.5Z" fill="#757575"/>
|
|
||||||
<path d="M3.94398 17.5C4.38924 17.5 4.75043 17.139 4.75043 16.6935V9.15312C4.75043 8.70771 4.38924 8.34668 3.94398 8.34668C3.49857 8.34668 3.13739 8.70771 3.13739 9.15312V16.6935C3.13739 17.139 3.49857 17.5 3.94398 17.5Z" fill="#757575"/>
|
|
||||||
<path d="M7.65361 17.5C7.2082 17.5 6.84717 17.139 6.84717 16.6935V9.15312C6.84717 8.70771 7.2082 8.34668 7.65361 8.34668C8.09902 8.34668 8.46005 8.70771 8.46005 9.15312V16.6935C8.46005 17.139 8.09902 17.5 7.65361 17.5Z" fill="#757575"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.1 KiB |
@ -1,39 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class EditChip extends StatelessWidget {
|
|
||||||
final String label;
|
|
||||||
final VoidCallback onTap;
|
|
||||||
final Color labelColor;
|
|
||||||
final Color backgroundColor;
|
|
||||||
final Color borderColor;
|
|
||||||
final double borderRadius;
|
|
||||||
|
|
||||||
const EditChip({
|
|
||||||
Key? key,
|
|
||||||
this.label = 'Edit',
|
|
||||||
required this.onTap,
|
|
||||||
this.labelColor = ColorsManager.spaceColor,
|
|
||||||
this.backgroundColor = ColorsManager.whiteColors,
|
|
||||||
this.borderColor = ColorsManager.spaceColor,
|
|
||||||
this.borderRadius = 16.0,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Chip(
|
|
||||||
label: Text(
|
|
||||||
label,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)
|
|
||||||
),
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
|
||||||
side: BorderSide(color: borderColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,6 +10,10 @@ class UserModel {
|
|||||||
final String? phoneNumber;
|
final String? phoneNumber;
|
||||||
final bool? isEmailVerified;
|
final bool? isEmailVerified;
|
||||||
final bool? isAgreementAccepted;
|
final bool? isAgreementAccepted;
|
||||||
|
final bool? hasAcceptedWebAgreement;
|
||||||
|
final DateTime? webAgreementAcceptedAt;
|
||||||
|
final UserRole? role;
|
||||||
|
|
||||||
UserModel({
|
UserModel({
|
||||||
required this.uuid,
|
required this.uuid,
|
||||||
required this.email,
|
required this.email,
|
||||||
@ -19,6 +23,9 @@ class UserModel {
|
|||||||
required this.phoneNumber,
|
required this.phoneNumber,
|
||||||
required this.isEmailVerified,
|
required this.isEmailVerified,
|
||||||
required this.isAgreementAccepted,
|
required this.isAgreementAccepted,
|
||||||
|
required this.hasAcceptedWebAgreement,
|
||||||
|
required this.webAgreementAcceptedAt,
|
||||||
|
required this.role,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory UserModel.fromJson(Map<String, dynamic> json) {
|
factory UserModel.fromJson(Map<String, dynamic> json) {
|
||||||
@ -31,6 +38,11 @@ class UserModel {
|
|||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'],
|
||||||
isEmailVerified: json['isEmailVerified'],
|
isEmailVerified: json['isEmailVerified'],
|
||||||
isAgreementAccepted: json['isAgreementAccepted'],
|
isAgreementAccepted: json['isAgreementAccepted'],
|
||||||
|
hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
|
||||||
|
webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
|
||||||
|
? DateTime.parse(json['webAgreementAcceptedAt'])
|
||||||
|
: null,
|
||||||
|
role: json['role'] != null ? UserRole.fromJson(json['role']) : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +53,9 @@ class UserModel {
|
|||||||
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
||||||
|
|
||||||
return UserModel(
|
return UserModel(
|
||||||
|
hasAcceptedWebAgreement: null,
|
||||||
|
role: null,
|
||||||
|
webAgreementAcceptedAt: null,
|
||||||
uuid: tempJson['uuid'].toString(),
|
uuid: tempJson['uuid'].toString(),
|
||||||
email: tempJson['email'],
|
email: tempJson['email'],
|
||||||
firstName: null,
|
firstName: null,
|
||||||
@ -65,3 +80,26 @@ class UserModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UserRole {
|
||||||
|
final String uuid;
|
||||||
|
final DateTime createdAt;
|
||||||
|
final DateTime updatedAt;
|
||||||
|
final String type;
|
||||||
|
|
||||||
|
UserRole({
|
||||||
|
required this.uuid,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.updatedAt,
|
||||||
|
required this.type,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory UserRole.fromJson(Map<String, dynamic> json) {
|
||||||
|
return UserRole(
|
||||||
|
uuid: json['uuid'],
|
||||||
|
createdAt: DateTime.parse(json['createdAt']),
|
||||||
|
updatedAt: DateTime.parse(json['updatedAt']),
|
||||||
|
type: json['type'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,14 +19,12 @@ class DefaultButton extends StatelessWidget {
|
|||||||
this.padding,
|
this.padding,
|
||||||
this.borderColor,
|
this.borderColor,
|
||||||
this.elevation,
|
this.elevation,
|
||||||
this.borderWidth = 1.0,
|
|
||||||
});
|
});
|
||||||
final void Function()? onPressed;
|
final void Function()? onPressed;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final double? height;
|
final double? height;
|
||||||
final bool isSecondary;
|
final bool isSecondary;
|
||||||
final double? borderRadius;
|
final double? borderRadius;
|
||||||
final double borderWidth;
|
|
||||||
final bool enabled;
|
final bool enabled;
|
||||||
final double? padding;
|
final double? padding;
|
||||||
final bool isDone;
|
final bool isDone;
|
||||||
@ -68,10 +66,7 @@ class DefaultButton extends StatelessWidget {
|
|||||||
}),
|
}),
|
||||||
shape: WidgetStateProperty.all(
|
shape: WidgetStateProperty.all(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
side: BorderSide(
|
side: BorderSide(color: borderColor ?? Colors.transparent),
|
||||||
color: borderColor ?? Colors.transparent,
|
|
||||||
width: borderWidth,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
class DeviceSubspace {
|
|
||||||
final String uuid;
|
|
||||||
final DateTime? createdAt;
|
|
||||||
final DateTime? updatedAt;
|
|
||||||
final String subspaceName;
|
|
||||||
final bool disabled;
|
|
||||||
|
|
||||||
DeviceSubspace({
|
|
||||||
required this.uuid,
|
|
||||||
this.createdAt,
|
|
||||||
this.updatedAt,
|
|
||||||
required this.subspaceName,
|
|
||||||
required this.disabled,
|
|
||||||
});
|
|
||||||
|
|
||||||
factory DeviceSubspace.fromJson(Map<String, dynamic> json) {
|
|
||||||
return DeviceSubspace(
|
|
||||||
uuid: json['uuid'] as String,
|
|
||||||
createdAt: json['createdAt'] != null
|
|
||||||
? DateTime.tryParse(json['createdAt'].toString())
|
|
||||||
: null,
|
|
||||||
updatedAt: json['updatedAt'] != null
|
|
||||||
? DateTime.tryParse(json['updatedAt'].toString())
|
|
||||||
: null,
|
|
||||||
subspaceName: json['subspaceName'] as String,
|
|
||||||
disabled: json['disabled'] as bool,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
return {
|
|
||||||
'uuid': uuid,
|
|
||||||
'createdAt': createdAt?.toIso8601String(),
|
|
||||||
'updatedAt': updatedAt?.toIso8601String(),
|
|
||||||
'subspaceName': subspaceName,
|
|
||||||
'disabled': disabled,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<DeviceSubspace> listFromJson(List<dynamic> jsonList) {
|
|
||||||
return jsonList.map((json) => DeviceSubspace.fromJson(json)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Map<String, dynamic>> listToJson(List<DeviceSubspace> subspaces) {
|
|
||||||
return subspaces.map((subspace) => subspace.toJson()).toList();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
||||||
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
|
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart';
|
||||||
@ -48,7 +47,6 @@ class AllDevicesModel {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
DevicesModelRoom? room;
|
DevicesModelRoom? room;
|
||||||
DeviceSubspace? subspace;
|
|
||||||
DevicesModelUnit? unit;
|
DevicesModelUnit? unit;
|
||||||
DeviceCommunityModel? community;
|
DeviceCommunityModel? community;
|
||||||
String? productUuid;
|
String? productUuid;
|
||||||
@ -79,7 +77,6 @@ class AllDevicesModel {
|
|||||||
|
|
||||||
AllDevicesModel({
|
AllDevicesModel({
|
||||||
this.room,
|
this.room,
|
||||||
this.subspace,
|
|
||||||
this.unit,
|
this.unit,
|
||||||
this.community,
|
this.community,
|
||||||
this.productUuid,
|
this.productUuid,
|
||||||
@ -113,9 +110,6 @@ class AllDevicesModel {
|
|||||||
room = (json['room'] != null && (json['room'] is Map))
|
room = (json['room'] != null && (json['room'] is Map))
|
||||||
? DevicesModelRoom.fromJson(json['room'])
|
? DevicesModelRoom.fromJson(json['room'])
|
||||||
: null;
|
: null;
|
||||||
subspace = (json['subspace'] != null && (json['subspace'] is Map))
|
|
||||||
? DeviceSubspace.fromJson(json['subspace'])
|
|
||||||
: null;
|
|
||||||
unit = (json['unit'] != null && (json['unit'] is Map))
|
unit = (json['unit'] != null && (json['unit'] is Map))
|
||||||
? DevicesModelUnit.fromJson(json['unit'])
|
? DevicesModelUnit.fromJson(json['unit'])
|
||||||
: null;
|
: null;
|
||||||
@ -294,9 +288,6 @@ SOS
|
|||||||
if (room != null) {
|
if (room != null) {
|
||||||
data['room'] = room!.toJson();
|
data['room'] = room!.toJson();
|
||||||
}
|
}
|
||||||
if (subspace != null) {
|
|
||||||
data['subspace'] = subspace!.toJson();
|
|
||||||
}
|
|
||||||
if (unit != null) {
|
if (unit != null) {
|
||||||
data['unit'] = unit!.toJson();
|
data['unit'] = unit!.toJson();
|
||||||
}
|
}
|
||||||
@ -339,7 +330,6 @@ SOS
|
|||||||
|
|
||||||
return other is AllDevicesModel &&
|
return other is AllDevicesModel &&
|
||||||
other.room == room &&
|
other.room == room &&
|
||||||
other.subspace == subspace &&
|
|
||||||
other.unit == unit &&
|
other.unit == unit &&
|
||||||
other.productUuid == productUuid &&
|
other.productUuid == productUuid &&
|
||||||
other.productType == productType &&
|
other.productType == productType &&
|
||||||
@ -370,7 +360,6 @@ SOS
|
|||||||
@override
|
@override
|
||||||
int get hashCode {
|
int get hashCode {
|
||||||
return room.hashCode ^
|
return room.hashCode ^
|
||||||
subspace.hashCode ^
|
|
||||||
unit.hashCode ^
|
unit.hashCode ^
|
||||||
productUuid.hashCode ^
|
productUuid.hashCode ^
|
||||||
productType.hashCode ^
|
productType.hashCode ^
|
||||||
|
@ -95,9 +95,8 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
]),
|
]),
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Space Name:',
|
_buildInfoRow('Space Name:', device.unit?.name ?? 'N/A'),
|
||||||
device.spaces?.firstOrNull?.spaceName ?? 'N/A'),
|
_buildInfoRow('Room:', device.room?.name ?? 'N/A'),
|
||||||
_buildInfoRow('Room:', device.subspace?.subspaceName ?? 'N/A'),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
@ -112,13 +111,9 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
),
|
),
|
||||||
_buildInfoRow(
|
_buildInfoRow(
|
||||||
'Battery Level:',
|
'Battery Level:',
|
||||||
device.batteryLevel != null
|
device.batteryLevel != null ? '${device.batteryLevel ?? 0}%' : "-",
|
||||||
? '${device.batteryLevel ?? 0}%'
|
|
||||||
: "-",
|
|
||||||
statusColor: device.batteryLevel != null
|
statusColor: device.batteryLevel != null
|
||||||
? (device.batteryLevel! < 20
|
? (device.batteryLevel! < 20 ? ColorsManager.red : ColorsManager.green)
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.green)
|
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -18,10 +18,15 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
List<Node> sourcesList = [];
|
List<Node> sourcesList = [];
|
||||||
List<Node> destinationsList = [];
|
List<Node> destinationsList = [];
|
||||||
UserModel? user;
|
UserModel? user;
|
||||||
|
String terms = '';
|
||||||
|
String policy = '';
|
||||||
|
|
||||||
HomeBloc() : super((HomeInitial())) {
|
HomeBloc() : super((HomeInitial())) {
|
||||||
on<CreateNewNode>(_createNode);
|
on<CreateNewNode>(_createNode);
|
||||||
on<FetchUserInfo>(_fetchUserInfo);
|
on<FetchUserInfo>(_fetchUserInfo);
|
||||||
|
on<FetchTermEvent>(_fetchTerms);
|
||||||
|
on<FetchPolicyEvent>(_fetchPolicy);
|
||||||
|
on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
|
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
|
||||||
@ -45,12 +50,45 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
var uuid =
|
var uuid =
|
||||||
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
user = await HomeApi().fetchUserInfo(uuid);
|
user = await HomeApi().fetchUserInfo(uuid);
|
||||||
|
add(FetchTermEvent());
|
||||||
emit(HomeInitial());
|
emit(HomeInitial());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future _fetchTerms(FetchTermEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
terms = await HomeApi().fetchTerms();
|
||||||
|
add(FetchPolicyEvent());
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _fetchPolicy(FetchPolicyEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
policy = await HomeApi().fetchPolicy();
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _confirmUserAgreement(
|
||||||
|
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
||||||
|
try {
|
||||||
|
emit(LoadingHome());
|
||||||
|
var uuid =
|
||||||
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
|
policy = await HomeApi().confirmUserAgreements(uuid);
|
||||||
|
emit(PolicyAgreement());
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static Future fetchUserInfo() async {
|
// static Future fetchUserInfo() async {
|
||||||
// try {
|
// try {
|
||||||
// var uuid =
|
// var uuid =
|
||||||
|
@ -20,4 +20,8 @@ class CreateNewNode extends HomeEvent {
|
|||||||
|
|
||||||
class FetchUserInfo extends HomeEvent {
|
class FetchUserInfo extends HomeEvent {
|
||||||
const FetchUserInfo();
|
const FetchUserInfo();
|
||||||
}
|
}class FetchTermEvent extends HomeEvent {}
|
||||||
|
|
||||||
|
class FetchPolicyEvent extends HomeEvent {}
|
||||||
|
|
||||||
|
class ConfirmUserAgreementEvent extends HomeEvent {}
|
@ -7,8 +7,12 @@ abstract class HomeState extends Equatable {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
class LoadingHome extends HomeState {}
|
||||||
|
|
||||||
class HomeInitial extends HomeState {}
|
class HomeInitial extends HomeState {}
|
||||||
|
class TermsAgreement extends HomeState {}
|
||||||
|
|
||||||
|
class PolicyAgreement extends HomeState {}
|
||||||
|
|
||||||
class HomeCounterState extends HomeState {
|
class HomeCounterState extends HomeState {
|
||||||
final int counter;
|
final int counter;
|
||||||
@ -24,3 +28,5 @@ class HomeUpdateTree extends HomeState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [graph, builder];
|
List<Object> get props => [graph, builder];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FetchTermEvent
|
176
lib/pages/home/view/agreement_and_privacy_dialog.dart
Normal file
176
lib/pages/home/view/agreement_and_privacy_dialog.dart
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
class AgreementAndPrivacyDialog extends StatefulWidget {
|
||||||
|
final String terms;
|
||||||
|
final String policy;
|
||||||
|
|
||||||
|
const AgreementAndPrivacyDialog({
|
||||||
|
super.key,
|
||||||
|
required this.terms,
|
||||||
|
required this.policy,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AgreementAndPrivacyDialogState createState() =>
|
||||||
|
_AgreementAndPrivacyDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
bool _isAtEnd = false;
|
||||||
|
int _currentPage = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_scrollController.addListener(_onScroll);
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) => _checkScrollRequirement());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _checkScrollRequirement() {
|
||||||
|
final scrollPosition = _scrollController.position;
|
||||||
|
if (scrollPosition.maxScrollExtent <= 0) {
|
||||||
|
setState(() {
|
||||||
|
_isAtEnd = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.removeListener(_onScroll);
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onScroll() {
|
||||||
|
if (_scrollController.position.atEdge) {
|
||||||
|
final isAtBottom = _scrollController.position.pixels ==
|
||||||
|
_scrollController.position.maxScrollExtent;
|
||||||
|
if (isAtBottom && !_isAtEnd) {
|
||||||
|
setState(() {
|
||||||
|
_isAtEnd = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String get _dialogTitle =>
|
||||||
|
_currentPage == 2 ? 'User Agreement' : 'Privacy Policy';
|
||||||
|
|
||||||
|
String get _dialogContent => _currentPage == 2 ? widget.terms : widget.policy;
|
||||||
|
|
||||||
|
Widget _buildScrollableContent() {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(40),
|
||||||
|
width: MediaQuery.of(context).size.width * 0.8,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.75,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey[200],
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
child: Scrollbar(
|
||||||
|
thumbVisibility: true,
|
||||||
|
trackVisibility: true,
|
||||||
|
interactive: true,
|
||||||
|
controller: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: const EdgeInsets.all(25),
|
||||||
|
child: Html(
|
||||||
|
data: _dialogContent,
|
||||||
|
onLinkTap: (url, attributes, element) async {
|
||||||
|
if (url != null) {
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
"body": Style(
|
||||||
|
fontSize: FontSize(14),
|
||||||
|
color: Colors.black87,
|
||||||
|
lineHeight: LineHeight(1.5),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildActionButton() {
|
||||||
|
final String buttonText = _currentPage == 2 ? "I Agree" : "Next";
|
||||||
|
|
||||||
|
return InkWell(
|
||||||
|
onTap: _isAtEnd
|
||||||
|
? () {
|
||||||
|
if (_currentPage == 1) {
|
||||||
|
setState(() {
|
||||||
|
_currentPage = 2;
|
||||||
|
_isAtEnd = false;
|
||||||
|
_scrollController.jumpTo(0);
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) => _checkScrollRequirement());
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Navigator.of(context).pop(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(
|
||||||
|
buttonText,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _isAtEnd ? ColorsManager.secondaryColor : Colors.grey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Text(
|
||||||
|
_dialogTitle,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: ColorsManager.secondaryColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
_buildScrollableContent(),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
AuthBloc.logout();
|
||||||
|
context.go(RoutesConst.auth);
|
||||||
|
},
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
_buildActionButton(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/home/view/agreement_and_privacy_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
|
||||||
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
import 'package:syncrow_web/pages/home/view/home_card.dart';
|
||||||
@ -9,16 +11,40 @@ import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
|||||||
|
|
||||||
class HomeWebPage extends StatelessWidget {
|
class HomeWebPage extends StatelessWidget {
|
||||||
const HomeWebPage({super.key});
|
const HomeWebPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Size size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
|
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvoked: (didPop) => false,
|
onPopInvoked: (didPop) => false,
|
||||||
child: BlocConsumer<HomeBloc, HomeState>(
|
child: BlocConsumer<HomeBloc, HomeState>(
|
||||||
listener: (BuildContext context, state) {},
|
listener: (BuildContext context, state) {
|
||||||
|
if (state is HomeInitial) {
|
||||||
|
if (homeBloc.user!.hasAcceptedWebAgreement == false) {
|
||||||
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AgreementAndPrivacyDialog(
|
||||||
|
terms: homeBloc.terms,
|
||||||
|
policy: homeBloc.policy,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).then((v) {
|
||||||
|
if (v != null) {
|
||||||
|
homeBloc.add(ConfirmUserAgreementEvent());
|
||||||
|
homeBloc.add(const FetchUserInfo());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
|
||||||
return WebScaffold(
|
return WebScaffold(
|
||||||
enableMenuSidebar: false,
|
enableMenuSidebar: false,
|
||||||
appBarTitle: Row(
|
appBarTitle: Row(
|
||||||
@ -52,7 +78,8 @@ class HomeWebPage extends StatelessWidget {
|
|||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: 3, //8
|
itemCount: 3, //8
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 4,
|
crossAxisCount: 4,
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
mainAxisSpacing: 20.0,
|
mainAxisSpacing: 20.0,
|
||||||
@ -64,7 +91,8 @@ class HomeWebPage extends StatelessWidget {
|
|||||||
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),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,74 +1,38 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
|
||||||
|
|
||||||
class AddDeviceTypeBloc extends Bloc<AddDeviceTypeEvent, AddDeviceState> {
|
class AddDeviceTypeBloc
|
||||||
AddDeviceTypeBloc() : super(AddDeviceInitial()) {
|
extends Bloc<AddDeviceTypeEvent, List<SelectedProduct>> {
|
||||||
on<InitializeDevice>(_onInitializeTagModels);
|
AddDeviceTypeBloc(List<SelectedProduct> initialProducts)
|
||||||
|
: super(initialProducts) {
|
||||||
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onInitializeTagModels(
|
|
||||||
InitializeDevice event, Emitter<AddDeviceState> emit) {
|
|
||||||
emit(AddDeviceLoaded(
|
|
||||||
selectedProducts: event.addedProducts,
|
|
||||||
initialTag: event.initialTags,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onUpdateProductCount(
|
void _onUpdateProductCount(
|
||||||
UpdateProductCountEvent event, Emitter<AddDeviceState> emit) {
|
UpdateProductCountEvent event, Emitter<List<SelectedProduct>> emit) {
|
||||||
final currentState = state;
|
final existingProduct = state.firstWhere(
|
||||||
|
|
||||||
if (currentState is AddDeviceLoaded) {
|
|
||||||
final existingProduct = currentState.selectedProducts.firstWhere(
|
|
||||||
(p) => p.productId == event.productId,
|
(p) => p.productId == event.productId,
|
||||||
orElse: () => SelectedProduct(
|
orElse: () => SelectedProduct(productId: event.productId, count: 0,productName: event.productName,product: event.product ),
|
||||||
productId: event.productId,
|
|
||||||
count: 0,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
List<SelectedProduct> updatedProducts;
|
|
||||||
|
|
||||||
if (event.count > 0) {
|
if (event.count > 0) {
|
||||||
if (!currentState.selectedProducts.contains(existingProduct)) {
|
if (!state.contains(existingProduct)) {
|
||||||
updatedProducts = [
|
emit([
|
||||||
...currentState.selectedProducts,
|
...state,
|
||||||
SelectedProduct(
|
SelectedProduct(productId: event.productId, count: event.count, productName: event.productName, product: event.product)
|
||||||
productId: event.productId,
|
]);
|
||||||
count: event.count,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
} else {
|
} else {
|
||||||
updatedProducts = currentState.selectedProducts.map((p) {
|
final updatedList = state.map((p) {
|
||||||
if (p.productId == event.productId) {
|
if (p.productId == event.productId) {
|
||||||
return SelectedProduct(
|
return SelectedProduct(productId: p.productId, count: event.count, productName: p.productName,product: p.product);
|
||||||
productId: p.productId,
|
|
||||||
count: event.count,
|
|
||||||
productName: p.productName,
|
|
||||||
product: p.product,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
emit(updatedList);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Remove the product if the count is 0
|
emit(state.where((p) => p.productId != event.productId).toList());
|
||||||
updatedProducts = currentState.selectedProducts
|
|
||||||
.where((p) => p.productId != event.productId)
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit the updated state
|
|
||||||
emit(AddDeviceLoaded(
|
|
||||||
selectedProducts: updatedProducts,
|
|
||||||
initialTag: currentState.initialTag));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
|
||||||
|
|
||||||
abstract class AddDeviceState extends Equatable {
|
|
||||||
const AddDeviceState();
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddDeviceInitial extends AddDeviceState {}
|
|
||||||
|
|
||||||
class AddDeviceLoading extends AddDeviceState {}
|
|
||||||
|
|
||||||
class AddDeviceLoaded extends AddDeviceState {
|
|
||||||
final List<SelectedProduct> selectedProducts;
|
|
||||||
final List<Tag> initialTag;
|
|
||||||
|
|
||||||
const AddDeviceLoaded({
|
|
||||||
required this.selectedProducts,
|
|
||||||
required this.initialTag,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [selectedProducts, initialTag];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddDeviceError extends AddDeviceState {
|
|
||||||
final String errorMessage;
|
|
||||||
|
|
||||||
const AddDeviceError(this.errorMessage);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [errorMessage];
|
|
||||||
}
|
|
@ -1,11 +1,7 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
|
||||||
|
|
||||||
abstract class AddDeviceTypeEvent extends Equatable {
|
abstract class AddDeviceTypeEvent extends Equatable {
|
||||||
const AddDeviceTypeEvent();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
@ -16,26 +12,8 @@ class UpdateProductCountEvent extends AddDeviceTypeEvent {
|
|||||||
final String productName;
|
final String productName;
|
||||||
final ProductModel product;
|
final ProductModel product;
|
||||||
|
|
||||||
UpdateProductCountEvent(
|
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
||||||
{required this.productId,
|
|
||||||
required this.count,
|
|
||||||
required this.productName,
|
|
||||||
required this.product});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [productId, count];
|
List<Object> get props => [productId, count];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InitializeDevice extends AddDeviceTypeEvent {
|
|
||||||
final List<Tag> initialTags;
|
|
||||||
final List<SelectedProduct> addedProducts;
|
|
||||||
|
|
||||||
const InitializeDevice({
|
|
||||||
this.initialTags = const [],
|
|
||||||
required this.addedProducts,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object> get props => [initialTags, addedProducts];
|
|
||||||
}
|
|
||||||
|
@ -1,19 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/scrollable_grid_view_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/scrollable_grid_view_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/action_button_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddDeviceTypeWidget extends StatelessWidget {
|
class AddDeviceTypeWidget extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
||||||
@ -22,12 +20,11 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
final List<Tag>? spaceTags;
|
final List<Tag>? spaceTags;
|
||||||
final List<String>? allTags;
|
final List<String>? allTags;
|
||||||
final String spaceName;
|
final String spaceName;
|
||||||
final bool isCreate;
|
final Function(List<Tag>,List<SubspaceModel>?)? onSave;
|
||||||
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
|
||||||
|
|
||||||
const AddDeviceTypeWidget(
|
const AddDeviceTypeWidget(
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.isCreate,
|
|
||||||
this.products,
|
this.products,
|
||||||
this.initialSelectedProducts,
|
this.initialSelectedProducts,
|
||||||
this.onProductsSelected,
|
this.onProductsSelected,
|
||||||
@ -47,22 +44,12 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
: 3;
|
: 3;
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AddDeviceTypeBloc()
|
create: (_) => AddDeviceTypeBloc(initialSelectedProducts ?? []),
|
||||||
..add(InitializeDevice(
|
|
||||||
initialTags: spaceTags ?? [],
|
|
||||||
addedProducts: initialSelectedProducts ?? [],
|
|
||||||
)),
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Add Devices'),
|
title: const Text('Add Devices'),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
content: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
content: SingleChildScrollView(
|
||||||
builder: (context, state) {
|
|
||||||
if (state is AddDeviceLoading) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
if (state is AddDeviceLoaded) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: size.width * 0.9,
|
width: size.width * 0.9,
|
||||||
height: size.height * 0.65,
|
height: size.height * 0.65,
|
||||||
@ -72,22 +59,15 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 20.0),
|
|
||||||
child: ScrollableGridViewWidget(
|
child: ScrollableGridViewWidget(
|
||||||
initialProductCounts: state.selectedProducts,
|
products: products, crossAxisCount: crossAxisCount),
|
||||||
products: products,
|
|
||||||
isCreate: isCreate,
|
|
||||||
crossAxisCount: crossAxisCount),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
|
||||||
return const SizedBox();
|
|
||||||
}),
|
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -98,27 +78,20 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SizedBox(
|
ActionButton(
|
||||||
width: 140,
|
label: 'Continue',
|
||||||
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final isDisabled = state is AddDeviceLoaded &&
|
|
||||||
state.selectedProducts.isEmpty;
|
|
||||||
return DefaultButton(
|
|
||||||
borderRadius: 10,
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
foregroundColor: isDisabled
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
? ColorsManager.whiteColorsWithOpacity
|
|
||||||
: ColorsManager.whiteColors,
|
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (state is AddDeviceLoaded &&
|
final currentState =
|
||||||
state.selectedProducts.isNotEmpty) {
|
context.read<AddDeviceTypeBloc>().state;
|
||||||
final initialTags =
|
Navigator.of(context).pop();
|
||||||
TagHelper.generateInitialForTags(
|
|
||||||
|
if (currentState.isNotEmpty) {
|
||||||
|
final initialTags = generateInitialTags(
|
||||||
spaceTags: spaceTags,
|
spaceTags: spaceTags,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
);
|
);
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
final dialogTitle = initialTags.isNotEmpty
|
final dialogTitle = initialTags.isNotEmpty
|
||||||
? 'Edit Device'
|
? 'Edit Device'
|
||||||
@ -129,23 +102,48 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
builder: (context) => AssignTagDialog(
|
builder: (context) => AssignTagDialog(
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
addedProducts: state.selectedProducts,
|
addedProducts: currentState,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
initialTags: initialTags,
|
initialTags: initialTags,
|
||||||
title: dialogTitle,
|
title: dialogTitle,
|
||||||
onSave: onSave),
|
onSave: (tags,subspaces){
|
||||||
|
onSave!(tags,subspaces);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Text('Next'),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Tag> generateInitialTags({
|
||||||
|
List<Tag>? spaceTags,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
}) {
|
||||||
|
final List<Tag> initialTags = [];
|
||||||
|
|
||||||
|
if (spaceTags != null) {
|
||||||
|
initialTags.addAll(spaceTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subspaces != null) {
|
||||||
|
for (var subspace in subspaces) {
|
||||||
|
if (subspace.tags != null) {
|
||||||
|
initialTags.addAll(
|
||||||
|
subspace.tags!.map(
|
||||||
|
(tag) => tag.copyWith(location: subspace.subspaceName),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,13 +13,12 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
class DeviceTypeTileWidget extends StatelessWidget {
|
class DeviceTypeTileWidget extends StatelessWidget {
|
||||||
final ProductModel product;
|
final ProductModel product;
|
||||||
final List<SelectedProduct> productCounts;
|
final List<SelectedProduct> productCounts;
|
||||||
final bool isCreate;
|
|
||||||
|
|
||||||
const DeviceTypeTileWidget(
|
const DeviceTypeTileWidget({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.product,
|
required this.product,
|
||||||
required this.productCounts,
|
required this.productCounts,
|
||||||
required this.isCreate});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -49,7 +48,7 @@ class DeviceTypeTileWidget extends StatelessWidget {
|
|||||||
DeviceNameWidget(name: product.name),
|
DeviceNameWidget(name: product.name),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
CounterWidget(
|
CounterWidget(
|
||||||
isCreate: isCreate,
|
isCreate: false,
|
||||||
initialCount: selectedProduct.count,
|
initialCount: selectedProduct.count,
|
||||||
onCountChanged: (newCount) {
|
onCountChanged: (newCount) {
|
||||||
context.read<AddDeviceTypeBloc>().add(
|
context.read<AddDeviceTypeBloc>().add(
|
||||||
|
@ -1,7 +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/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
@ -10,14 +9,13 @@ class ScrollableGridViewWidget extends StatelessWidget {
|
|||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final int crossAxisCount;
|
final int crossAxisCount;
|
||||||
final List<SelectedProduct>? initialProductCounts;
|
final List<SelectedProduct>? initialProductCounts;
|
||||||
final bool isCreate;
|
|
||||||
|
|
||||||
const ScrollableGridViewWidget(
|
const ScrollableGridViewWidget({
|
||||||
{super.key,
|
super.key,
|
||||||
required this.products,
|
required this.products,
|
||||||
required this.crossAxisCount,
|
required this.crossAxisCount,
|
||||||
this.initialProductCounts,
|
this.initialProductCounts,
|
||||||
required this.isCreate});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -26,11 +24,8 @@ class ScrollableGridViewWidget extends StatelessWidget {
|
|||||||
return Scrollbar(
|
return Scrollbar(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
thumbVisibility: true,
|
thumbVisibility: true,
|
||||||
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
child: BlocBuilder<AddDeviceTypeBloc, List<SelectedProduct>>(
|
||||||
builder: (context, state) {
|
builder: (context, productCounts) {
|
||||||
final productCounts = state is AddDeviceLoaded
|
|
||||||
? state.selectedProducts
|
|
||||||
: <SelectedProduct>[];
|
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
@ -47,7 +42,6 @@ class ScrollableGridViewWidget extends StatelessWidget {
|
|||||||
|
|
||||||
return DeviceTypeTileWidget(
|
return DeviceTypeTileWidget(
|
||||||
product: product,
|
product: product,
|
||||||
isCreate: isCreate,
|
|
||||||
productCounts: initialProductCount != null
|
productCounts: initialProductCount != null
|
||||||
? [...productCounts, initialProductCount]
|
? [...productCounts, initialProductCount]
|
||||||
: productCounts,
|
: productCounts,
|
||||||
|
@ -5,15 +5,12 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_mod
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
|
||||||
import 'package:syncrow_web/services/product_api.dart';
|
import 'package:syncrow_web/services/product_api.dart';
|
||||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
|
||||||
|
|
||||||
class SpaceManagementBloc
|
class SpaceManagementBloc
|
||||||
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||||
@ -179,7 +176,6 @@ class SpaceManagementBloc
|
|||||||
final updatedCommunities =
|
final updatedCommunities =
|
||||||
await Future.wait(communities.map((community) async {
|
await Future.wait(communities.map((community) async {
|
||||||
final spaces = await _fetchSpacesForCommunity(community.uuid);
|
final spaces = await _fetchSpacesForCommunity(community.uuid);
|
||||||
|
|
||||||
return CommunityModel(
|
return CommunityModel(
|
||||||
uuid: community.uuid,
|
uuid: community.uuid,
|
||||||
createdAt: community.createdAt,
|
createdAt: community.createdAt,
|
||||||
@ -314,7 +310,6 @@ class SpaceManagementBloc
|
|||||||
SelectSpaceEvent event,
|
SelectSpaceEvent event,
|
||||||
Emitter<SpaceManagementState> emit,
|
Emitter<SpaceManagementState> emit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
_handleCommunitySpaceStateUpdate(
|
_handleCommunitySpaceStateUpdate(
|
||||||
emit: emit,
|
emit: emit,
|
||||||
selectedCommunity: event.selectedCommunity,
|
selectedCommunity: event.selectedCommunity,
|
||||||
@ -346,7 +341,7 @@ class SpaceManagementBloc
|
|||||||
products: _cachedProducts ?? [],
|
products: _cachedProducts ?? [],
|
||||||
selectedCommunity: selectedCommunity,
|
selectedCommunity: selectedCommunity,
|
||||||
selectedSpace: selectedSpace,
|
selectedSpace: selectedSpace,
|
||||||
spaceModels: spaceModels));
|
spaceModels: spaceModels ?? []));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(SpaceManagementError('Error updating state: $e'));
|
emit(SpaceManagementError('Error updating state: $e'));
|
||||||
@ -433,76 +428,6 @@ class SpaceManagementBloc
|
|||||||
for (var space in orderedSpaces) {
|
for (var space in orderedSpaces) {
|
||||||
try {
|
try {
|
||||||
if (space.uuid != null && space.uuid!.isNotEmpty) {
|
if (space.uuid != null && space.uuid!.isNotEmpty) {
|
||||||
List<TagModelUpdate> tagUpdates = [];
|
|
||||||
|
|
||||||
final prevSpace = await _api.getSpace(communityUuid, space.uuid!);
|
|
||||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
|
||||||
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
|
||||||
final List<SubspaceModel>? newSubspaces = space.subspaces;
|
|
||||||
|
|
||||||
tagUpdates = processTagUpdates(prevSpace?.tags, space.tags);
|
|
||||||
|
|
||||||
if (prevSubspaces != null || newSubspaces != null) {
|
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
|
||||||
for (var prevSubspace in prevSubspaces) {
|
|
||||||
final existsInNew = newSubspaces
|
|
||||||
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
|
||||||
if (!existsInNew) {
|
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.delete, uuid: prevSubspace.uuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (prevSubspaces != null && newSubspaces == null) {
|
|
||||||
for (var prevSubspace in prevSubspaces) {
|
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.delete, uuid: prevSubspace.uuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSubspaces != null) {
|
|
||||||
for (var newSubspace in newSubspaces) {
|
|
||||||
// Tag without UUID
|
|
||||||
if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
|
|
||||||
final List<TagModelUpdate> tagUpdates = [];
|
|
||||||
|
|
||||||
if (newSubspace.tags != null) {
|
|
||||||
for (var tag in newSubspace.tags!) {
|
|
||||||
tagUpdates.add(TagModelUpdate(
|
|
||||||
action: Action.add,
|
|
||||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
|
||||||
tag: tag.tag,
|
|
||||||
productUuid: tag.product?.uuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.add,
|
|
||||||
subspaceName: newSubspace.subspaceName,
|
|
||||||
tags: tagUpdates));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
|
||||||
final newSubspaceMap = {
|
|
||||||
for (var subspace in newSubspaces) subspace.uuid: subspace
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var prevSubspace in prevSubspaces) {
|
|
||||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
|
||||||
|
|
||||||
if (newSubspace != null) {
|
|
||||||
final List<TagModelUpdate> tagSubspaceUpdates =
|
|
||||||
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
|
||||||
action: Action.update,
|
|
||||||
uuid: newSubspace.uuid,
|
|
||||||
subspaceName: newSubspace.subspaceName,
|
|
||||||
tags: tagSubspaceUpdates));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final response = await _api.updateSpace(
|
final response = await _api.updateSpace(
|
||||||
communityId: communityUuid,
|
communityId: communityUuid,
|
||||||
spaceId: space.uuid!,
|
spaceId: space.uuid!,
|
||||||
@ -511,8 +436,6 @@ class SpaceManagementBloc
|
|||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
subspaces: subspaceUpdates,
|
|
||||||
tags: tagUpdates,
|
|
||||||
direction: space.incomingConnection?.direction,
|
direction: space.incomingConnection?.direction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -612,79 +535,4 @@ class SpaceManagementBloc
|
|||||||
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<TagModelUpdate> processTagUpdates(
|
|
||||||
List<Tag>? prevTags,
|
|
||||||
List<Tag>? newTags,
|
|
||||||
) {
|
|
||||||
final List<TagModelUpdate> tagUpdates = [];
|
|
||||||
final processedTags = <String?>{};
|
|
||||||
|
|
||||||
if (prevTags == null && newTags != null) {
|
|
||||||
for (var newTag in newTags) {
|
|
||||||
tagUpdates.add(TagModelUpdate(
|
|
||||||
action: Action.add,
|
|
||||||
tag: newTag.tag,
|
|
||||||
uuid: newTag.uuid,
|
|
||||||
productUuid: newTag.product?.uuid,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return tagUpdates;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTags != null || prevTags != null) {
|
|
||||||
// Case 1: Tags deleted
|
|
||||||
if (prevTags != null && newTags != null) {
|
|
||||||
for (var prevTag in prevTags) {
|
|
||||||
final existsInNew =
|
|
||||||
newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
|
||||||
if (!existsInNew) {
|
|
||||||
tagUpdates
|
|
||||||
.add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (prevTags != null && newTags == null) {
|
|
||||||
for (var prevTag in prevTags) {
|
|
||||||
tagUpdates
|
|
||||||
.add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 2: Tags added
|
|
||||||
if (newTags != null) {
|
|
||||||
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
|
|
||||||
|
|
||||||
for (var newTag in newTags) {
|
|
||||||
// Tag without UUID
|
|
||||||
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
|
|
||||||
!processedTags.contains(newTag.tag)) {
|
|
||||||
tagUpdates.add(TagModelUpdate(
|
|
||||||
action: Action.add,
|
|
||||||
tag: newTag.tag,
|
|
||||||
uuid: newTag.uuid == '' ? null : newTag.uuid,
|
|
||||||
productUuid: newTag.product?.uuid));
|
|
||||||
processedTags.add(newTag.tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case 3: Tags updated
|
|
||||||
if (prevTags != null && newTags != null) {
|
|
||||||
final newTagMap = {for (var tag in newTags) tag.uuid: tag};
|
|
||||||
|
|
||||||
for (var prevTag in prevTags) {
|
|
||||||
final newTag = newTagMap[prevTag.uuid];
|
|
||||||
if (newTag != null) {
|
|
||||||
tagUpdates.add(TagModelUpdate(
|
|
||||||
action: Action.update,
|
|
||||||
uuid: newTag.uuid,
|
|
||||||
tag: newTag.tag,
|
|
||||||
));
|
|
||||||
} else {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tagUpdates;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
abstract class BaseTag {
|
|
||||||
String? uuid;
|
|
||||||
String? tag;
|
|
||||||
final ProductModel? product;
|
|
||||||
String internalId;
|
|
||||||
String? location;
|
|
||||||
|
|
||||||
BaseTag({
|
|
||||||
this.uuid,
|
|
||||||
required this.tag,
|
|
||||||
this.product,
|
|
||||||
String? internalId,
|
|
||||||
this.location,
|
|
||||||
}) : internalId = internalId ?? const Uuid().v4();
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson();
|
|
||||||
BaseTag copyWith({
|
|
||||||
String? tag,
|
|
||||||
ProductModel? product,
|
|
||||||
String? location,
|
|
||||||
String? internalId,
|
|
||||||
});
|
|
||||||
}
|
|
@ -66,6 +66,7 @@ class SpaceModel {
|
|||||||
final instance = SpaceModel(
|
final instance = SpaceModel(
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
uuid: json['uuid'] ?? '',
|
uuid: json['uuid'] ?? '',
|
||||||
|
spaceTuyaUuid: json['spaceTuyaUuid'],
|
||||||
name: json['spaceName'],
|
name: json['spaceName'],
|
||||||
isPrivate: json['isPrivate'] ?? false,
|
isPrivate: json['isPrivate'] ?? false,
|
||||||
invitationCode: json['invitationCode'],
|
invitationCode: json['invitationCode'],
|
||||||
@ -95,9 +96,6 @@ class SpaceModel {
|
|||||||
icon: json['icon'] ?? Assets.location,
|
icon: json['icon'] ?? Assets.location,
|
||||||
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
||||||
isHovered: false,
|
isHovered: false,
|
||||||
spaceModel: json['spaceModel'] != null
|
|
||||||
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
|
||||||
: null,
|
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['tags'] 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>))
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
|
||||||
import 'tag.dart';
|
import 'tag.dart';
|
||||||
|
|
||||||
@ -9,24 +8,19 @@ class SubspaceModel {
|
|||||||
String subspaceName;
|
String subspaceName;
|
||||||
final bool disabled;
|
final bool disabled;
|
||||||
List<Tag>? tags;
|
List<Tag>? tags;
|
||||||
String internalId;
|
|
||||||
|
|
||||||
SubspaceModel({
|
SubspaceModel({
|
||||||
this.uuid,
|
this.uuid,
|
||||||
required this.subspaceName,
|
required this.subspaceName,
|
||||||
required this.disabled,
|
required this.disabled,
|
||||||
this.tags,
|
this.tags,
|
||||||
String? internalId,
|
});
|
||||||
}) : internalId = internalId ?? const Uuid().v4();
|
|
||||||
|
|
||||||
factory SubspaceModel.fromJson(Map<String, dynamic> json) {
|
factory SubspaceModel.fromJson(Map<String, dynamic> json) {
|
||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
|
||||||
|
|
||||||
return SubspaceModel(
|
return SubspaceModel(
|
||||||
uuid: json['uuid'] ?? '',
|
uuid: json['uuid'] ?? '',
|
||||||
subspaceName: json['subspaceName'] ?? '',
|
subspaceName: json['subspaceName'] ?? '',
|
||||||
disabled: json['disabled'] ?? false,
|
disabled: json['disabled'] ?? false,
|
||||||
internalId: internalId,
|
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['tags'] as List<dynamic>?)
|
||||||
?.map((item) => Tag.fromJson(item))
|
?.map((item) => Tag.fromJson(item))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class Tag extends BaseTag {
|
class Tag {
|
||||||
Tag({
|
String? uuid;
|
||||||
String? uuid,
|
String? tag;
|
||||||
required String? tag,
|
final ProductModel? product;
|
||||||
ProductModel? product,
|
String internalId;
|
||||||
|
String? location;
|
||||||
|
|
||||||
|
Tag(
|
||||||
|
{this.uuid,
|
||||||
|
required this.tag,
|
||||||
|
this.product,
|
||||||
String? internalId,
|
String? internalId,
|
||||||
String? location,
|
this.location})
|
||||||
}) : super(
|
: internalId = internalId ?? const Uuid().v4();
|
||||||
uuid: uuid,
|
|
||||||
tag: tag,
|
|
||||||
product: product,
|
|
||||||
internalId: internalId,
|
|
||||||
location: location,
|
|
||||||
);
|
|
||||||
|
|
||||||
factory Tag.fromJson(Map<String, dynamic> json) {
|
factory Tag.fromJson(Map<String, dynamic> json) {
|
||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||||
@ -32,19 +31,15 @@ class Tag extends BaseTag {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Tag copyWith({
|
Tag copyWith({
|
||||||
String? tag,
|
String? tag,
|
||||||
ProductModel? product,
|
ProductModel? product,
|
||||||
String? location,
|
String? location,
|
||||||
String? internalId,
|
|
||||||
}) {
|
}) {
|
||||||
return Tag(
|
return Tag(
|
||||||
uuid: uuid,
|
|
||||||
tag: tag ?? this.tag,
|
tag: tag ?? this.tag,
|
||||||
product: product ?? this.product,
|
product: product ?? this.product,
|
||||||
location: location ?? this.location,
|
location: location ?? this.location,
|
||||||
internalId: internalId ?? this.internalId,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_button.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
|
||||||
|
|
||||||
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
|
||||||
const CommunityStructureHeaderActionButtons(
|
|
||||||
{super.key,
|
|
||||||
required this.theme,
|
|
||||||
required this.isSave,
|
|
||||||
required this.onSave,
|
|
||||||
required this.onDelete,
|
|
||||||
required this.selectedSpace,
|
|
||||||
required this.onDuplicate,
|
|
||||||
required this.onEdit});
|
|
||||||
|
|
||||||
final ThemeData theme;
|
|
||||||
final bool isSave;
|
|
||||||
final VoidCallback onSave;
|
|
||||||
final VoidCallback onDelete;
|
|
||||||
final VoidCallback onDuplicate;
|
|
||||||
final VoidCallback onEdit;
|
|
||||||
|
|
||||||
final SpaceModel? selectedSpace;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final canShowActions = selectedSpace != null &&
|
|
||||||
selectedSpace?.status != SpaceStatus.deleted &&
|
|
||||||
selectedSpace?.status != SpaceStatus.parentDeleted;
|
|
||||||
|
|
||||||
return Wrap(
|
|
||||||
alignment: WrapAlignment.end,
|
|
||||||
spacing: 10,
|
|
||||||
children: [
|
|
||||||
if (isSave)
|
|
||||||
CommunityStructureHeaderButton(
|
|
||||||
label: "Save",
|
|
||||||
icon: const Icon(Icons.save,
|
|
||||||
size: 18, color: ColorsManager.spaceColor),
|
|
||||||
onPressed: onSave,
|
|
||||||
theme: theme,
|
|
||||||
),
|
|
||||||
if (canShowActions) ...[
|
|
||||||
CommunityStructureHeaderButton(
|
|
||||||
label: "Edit",
|
|
||||||
svgAsset: Assets.editSpace,
|
|
||||||
onPressed: onEdit,
|
|
||||||
theme: theme,
|
|
||||||
),
|
|
||||||
CommunityStructureHeaderButton(
|
|
||||||
label: "Duplicate",
|
|
||||||
svgAsset: Assets.duplicate,
|
|
||||||
onPressed: onDuplicate,
|
|
||||||
theme: theme,
|
|
||||||
),
|
|
||||||
CommunityStructureHeaderButton(
|
|
||||||
label: "Delete",
|
|
||||||
svgAsset: Assets.spaceDelete,
|
|
||||||
onPressed: onDelete,
|
|
||||||
theme: theme,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class CommunityStructureHeaderButton extends StatelessWidget {
|
|
||||||
const CommunityStructureHeaderButton({
|
|
||||||
super.key,
|
|
||||||
required this.label,
|
|
||||||
this.icon,
|
|
||||||
required this.onPressed,
|
|
||||||
this.svgAsset,
|
|
||||||
required this.theme,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String label;
|
|
||||||
final Widget? icon;
|
|
||||||
final VoidCallback onPressed;
|
|
||||||
final String? svgAsset;
|
|
||||||
final ThemeData theme;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
const double buttonHeight = 40;
|
|
||||||
return ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 130,
|
|
||||||
minHeight: buttonHeight,
|
|
||||||
),
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: onPressed,
|
|
||||||
borderWidth: 2,
|
|
||||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
|
||||||
foregroundColor: ColorsManager.blackColor,
|
|
||||||
borderRadius: 12.0,
|
|
||||||
padding: 2.0,
|
|
||||||
height: buttonHeight,
|
|
||||||
elevation: 0,
|
|
||||||
borderColor: ColorsManager.lightGrayColor,
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (icon != null) icon!,
|
|
||||||
if (svgAsset != null)
|
|
||||||
SvgPicture.asset(
|
|
||||||
svgAsset!,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
label,
|
|
||||||
style: theme.textTheme.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.blackColor, fontSize: 14),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_action_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.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';
|
||||||
@ -14,9 +14,6 @@ class CommunityStructureHeader extends StatefulWidget {
|
|||||||
final TextEditingController nameController;
|
final TextEditingController nameController;
|
||||||
final VoidCallback onSave;
|
final VoidCallback onSave;
|
||||||
final VoidCallback onDelete;
|
final VoidCallback onDelete;
|
||||||
final VoidCallback onEdit;
|
|
||||||
final VoidCallback onDuplicate;
|
|
||||||
|
|
||||||
final VoidCallback onEditName;
|
final VoidCallback onEditName;
|
||||||
final ValueChanged<String> onNameSubmitted;
|
final ValueChanged<String> onNameSubmitted;
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
@ -35,9 +32,7 @@ class CommunityStructureHeader extends StatefulWidget {
|
|||||||
required this.onNameSubmitted,
|
required this.onNameSubmitted,
|
||||||
this.community,
|
this.community,
|
||||||
required this.communities,
|
required this.communities,
|
||||||
this.selectedSpace,
|
this.selectedSpace});
|
||||||
required this.onDuplicate,
|
|
||||||
required this.onEdit});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CommunityStructureHeader> createState() =>
|
State<CommunityStructureHeader> createState() =>
|
||||||
@ -146,18 +141,70 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
CommunityStructureHeaderActionButtons(
|
_buildActionButtons(theme),
|
||||||
theme: theme,
|
|
||||||
isSave: widget.isSave,
|
|
||||||
onSave: widget.onSave,
|
|
||||||
onDelete: widget.onDelete,
|
|
||||||
onDuplicate: widget.onDuplicate,
|
|
||||||
onEdit: widget.onEdit,
|
|
||||||
selectedSpace: widget.selectedSpace,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildActionButtons(ThemeData theme) {
|
||||||
|
return Wrap(
|
||||||
|
alignment: WrapAlignment.end,
|
||||||
|
spacing: 10,
|
||||||
|
children: [
|
||||||
|
if (widget.isSave)
|
||||||
|
_buildButton(
|
||||||
|
label: "Save",
|
||||||
|
icon: const Icon(Icons.save,
|
||||||
|
size: 18, color: ColorsManager.spaceColor),
|
||||||
|
onPressed: widget.onSave,
|
||||||
|
theme: theme),
|
||||||
|
if(widget.selectedSpace!= null)
|
||||||
|
_buildButton(
|
||||||
|
label: "Delete",
|
||||||
|
icon: const Icon(Icons.delete,
|
||||||
|
size: 18, color: ColorsManager.warningRed),
|
||||||
|
onPressed: widget.onDelete,
|
||||||
|
theme: theme),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildButton(
|
||||||
|
{required String label,
|
||||||
|
required Widget icon,
|
||||||
|
required VoidCallback onPressed,
|
||||||
|
required ThemeData theme}) {
|
||||||
|
const double buttonHeight = 30;
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(maxWidth: 80, minHeight: buttonHeight),
|
||||||
|
child: DefaultButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
|
foregroundColor: ColorsManager.blackColor,
|
||||||
|
borderRadius: 8.0,
|
||||||
|
padding: 2.0,
|
||||||
|
height: buttonHeight,
|
||||||
|
elevation: 0,
|
||||||
|
borderColor: ColorsManager.lightGrayColor,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
icon,
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
label,
|
||||||
|
style: theme.textTheme.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.blackColor),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
|
|
||||||
// Syncrow project imports
|
// Syncrow project imports
|
||||||
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
@ -19,10 +17,8 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/blank_com
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/duplicate_process_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/space_helper.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/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
@ -137,8 +133,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
onSave: _saveSpaces,
|
onSave: _saveSpaces,
|
||||||
selectedSpace: widget.selectedSpace,
|
selectedSpace: widget.selectedSpace,
|
||||||
onDelete: _onDelete,
|
onDelete: _onDelete,
|
||||||
onDuplicate: () => {_onDuplicate(context)},
|
|
||||||
onEdit: () => {_showEditSpaceDialog()},
|
|
||||||
onEditName: () {
|
onEditName: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
isEditingName = !isEditingName;
|
isEditingName = !isEditingName;
|
||||||
@ -215,6 +209,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
opacity: isHighlighted ? 1.0 : 0.3,
|
opacity: isHighlighted ? 1.0 : 0.3,
|
||||||
child: SpaceContainerWidget(
|
child: SpaceContainerWidget(
|
||||||
index: index,
|
index: index,
|
||||||
|
onDoubleTap: () {
|
||||||
|
_showEditSpaceDialog(spaces[index]);
|
||||||
|
},
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_selectSpace(context, spaces[index]);
|
_selectSpace(context, spaces[index]);
|
||||||
},
|
},
|
||||||
@ -295,7 +292,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return CreateSpaceDialog(
|
return CreateSpaceDialog(
|
||||||
products: widget.products,
|
products: widget.products,
|
||||||
spaceModels: widget.spaceModels,
|
spaceModels: widget.spaceModels,
|
||||||
allTags: _getAllTagValues(spaces),
|
|
||||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||||
onCreateSpace: (String name,
|
onCreateSpace: (String name,
|
||||||
String icon,
|
String icon,
|
||||||
@ -332,6 +328,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
parentSpace.addOutgoingConnection(newConnection);
|
parentSpace.addOutgoingConnection(newConnection);
|
||||||
parentSpace.children.add(newSpace);
|
parentSpace.children.add(newSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
spaces.add(newSpace);
|
spaces.add(newSpace);
|
||||||
_updateNodePosition(newSpace, newSpace.position);
|
_updateNodePosition(newSpace, newSpace.position);
|
||||||
});
|
});
|
||||||
@ -341,24 +338,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showEditSpaceDialog() {
|
void _showEditSpaceDialog(SpaceModel space) {
|
||||||
if (widget.selectedSpace != null) {
|
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CreateSpaceDialog(
|
return CreateSpaceDialog(
|
||||||
products: widget.products,
|
products: widget.products,
|
||||||
spaceModels: widget.spaceModels,
|
name: space.name,
|
||||||
name: widget.selectedSpace!.name,
|
icon: space.icon,
|
||||||
icon: widget.selectedSpace!.icon,
|
editSpace: space,
|
||||||
parentSpace: SpaceHelper.findSpaceByInternalId(
|
|
||||||
widget.selectedSpace?.parent?.internalId, spaces),
|
|
||||||
editSpace: widget.selectedSpace,
|
|
||||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
|
||||||
tags: widget.selectedSpace?.tags,
|
|
||||||
subspaces: widget.selectedSpace?.subspaces,
|
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
allTags: _getAllTagValues(spaces),
|
|
||||||
onCreateSpace: (String name,
|
onCreateSpace: (String name,
|
||||||
String icon,
|
String icon,
|
||||||
List<SelectedProduct> selectedProducts,
|
List<SelectedProduct> selectedProducts,
|
||||||
@ -367,33 +356,22 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
List<Tag>? tags) {
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Update the space's properties
|
// Update the space's properties
|
||||||
widget.selectedSpace!.name = name;
|
space.name = name;
|
||||||
widget.selectedSpace!.icon = icon;
|
space.icon = icon;
|
||||||
widget.selectedSpace!.spaceModel = spaceModel;
|
space.spaceModel = spaceModel;
|
||||||
widget.selectedSpace!.subspaces = subspaces;
|
|
||||||
widget.selectedSpace!.tags = tags;
|
|
||||||
|
|
||||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
|
||||||
widget.selectedSpace!.status =
|
|
||||||
SpaceStatus.modified; // Mark as modified
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var space in spaces) {
|
|
||||||
if (space.internalId == widget.selectedSpace?.internalId) {
|
|
||||||
space.status = SpaceStatus.modified;
|
|
||||||
space.subspaces = subspaces;
|
space.subspaces = subspaces;
|
||||||
space.tags = tags;
|
space.tags = tags;
|
||||||
space.name = name;
|
|
||||||
}
|
if (space.status != SpaceStatus.newSpace) {
|
||||||
|
space.status = SpaceStatus.modified; // Mark as modified
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
key: Key(widget.selectedSpace!.name),
|
key: Key(space.name),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void _handleHoverChanged(int index, bool isHovered) {
|
void _handleHoverChanged(int index, bool isHovered) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -464,6 +442,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
if (spacesToSave.isEmpty) {
|
if (spacesToSave.isEmpty) {
|
||||||
|
debugPrint("No new or modified spaces to save.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,201 +545,4 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
space.status == SpaceStatus.modified ||
|
space.status == SpaceStatus.modified ||
|
||||||
space.status == SpaceStatus.deleted);
|
space.status == SpaceStatus.deleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onDuplicate(BuildContext parentContext) {
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
|
|
||||||
if (widget.selectedSpace != null) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertDialog(
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
title: Text(
|
|
||||||
"Duplicate Space",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headlineLarge
|
|
||||||
?.copyWith(color: ColorsManager.blackColor),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: screenWidth * 0.4,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text("Are you sure you want to duplicate?",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
Text("All the child spaces will be duplicated.",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(color: ColorsManager.lightGrayColor)),
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: CancelButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
label: "Cancel",
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
barrierDismissible: false,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return DuplicateProcessDialog(
|
|
||||||
onDuplicate: () {
|
|
||||||
_duplicateSpace(widget.selectedSpace!);
|
|
||||||
_deselectSpace(parentContext);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _duplicateSpace(SpaceModel space) {
|
|
||||||
final Map<SpaceModel, SpaceModel> originalToDuplicate = {};
|
|
||||||
const double horizontalGap = 200.0;
|
|
||||||
const double verticalGap = 100.0;
|
|
||||||
|
|
||||||
final Map<String, int> nameCounters = {};
|
|
||||||
|
|
||||||
SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition,
|
|
||||||
SpaceModel? duplicatedParent) {
|
|
||||||
Offset newPosition = parentPosition + Offset(horizontalGap, 0);
|
|
||||||
|
|
||||||
while (spaces.any((s) =>
|
|
||||||
(s.position - newPosition).distance < horizontalGap &&
|
|
||||||
s.status != SpaceStatus.deleted)) {
|
|
||||||
newPosition += Offset(horizontalGap, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
final duplicatedName =
|
|
||||||
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
|
||||||
|
|
||||||
final duplicated = SpaceModel(
|
|
||||||
name: duplicatedName,
|
|
||||||
icon: original.icon,
|
|
||||||
position: newPosition,
|
|
||||||
isPrivate: original.isPrivate,
|
|
||||||
children: [],
|
|
||||||
status: SpaceStatus.newSpace,
|
|
||||||
parent: duplicatedParent,
|
|
||||||
spaceModel: original.spaceModel,
|
|
||||||
subspaces: original.subspaces,
|
|
||||||
tags: original.tags,
|
|
||||||
);
|
|
||||||
|
|
||||||
originalToDuplicate[original] = duplicated;
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
spaces.add(duplicated);
|
|
||||||
_updateNodePosition(duplicated, duplicated.position);
|
|
||||||
|
|
||||||
if (duplicatedParent != null) {
|
|
||||||
final newConnection = Connection(
|
|
||||||
startSpace: duplicatedParent,
|
|
||||||
endSpace: duplicated,
|
|
||||||
direction: "down",
|
|
||||||
);
|
|
||||||
connections.add(newConnection);
|
|
||||||
duplicated.incomingConnection = newConnection;
|
|
||||||
duplicatedParent.addOutgoingConnection(newConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (original.parent != null && duplicatedParent == null) {
|
|
||||||
final originalParent = original.parent!;
|
|
||||||
final duplicatedParent =
|
|
||||||
originalToDuplicate[originalParent] ?? originalParent;
|
|
||||||
|
|
||||||
final parentConnection = Connection(
|
|
||||||
startSpace: duplicatedParent,
|
|
||||||
endSpace: duplicated,
|
|
||||||
direction: original.incomingConnection?.direction ?? "down",
|
|
||||||
);
|
|
||||||
|
|
||||||
connections.add(parentConnection);
|
|
||||||
duplicated.incomingConnection = parentConnection;
|
|
||||||
duplicatedParent.addOutgoingConnection(parentConnection);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final childrenWithDownDirection = original.children
|
|
||||||
.where((child) =>
|
|
||||||
child.incomingConnection?.direction == "down" &&
|
|
||||||
child.status != SpaceStatus.deleted)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
Offset childStartPosition = childrenWithDownDirection.length == 1
|
|
||||||
? duplicated.position
|
|
||||||
: newPosition + Offset(0, verticalGap);
|
|
||||||
|
|
||||||
for (final child in original.children) {
|
|
||||||
final isDownDirection =
|
|
||||||
child.incomingConnection?.direction == "down" ?? false;
|
|
||||||
|
|
||||||
if (isDownDirection && childrenWithDownDirection.length == 1) {
|
|
||||||
// Place the only "down" child vertically aligned with the parent
|
|
||||||
childStartPosition = duplicated.position + Offset(0, verticalGap);
|
|
||||||
} else if (!isDownDirection) {
|
|
||||||
// Position children with other directions horizontally
|
|
||||||
childStartPosition = duplicated.position + Offset(horizontalGap, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
final duplicatedChild =
|
|
||||||
duplicateRecursive(child, childStartPosition, duplicated);
|
|
||||||
duplicated.children.add(duplicatedChild);
|
|
||||||
childStartPosition += Offset(0, verticalGap);
|
|
||||||
}
|
|
||||||
|
|
||||||
return duplicated;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (space.parent == null) {
|
|
||||||
duplicateRecursive(space, space.position, null);
|
|
||||||
} else {
|
|
||||||
final duplicatedParent =
|
|
||||||
originalToDuplicate[space.parent!] ?? space.parent!;
|
|
||||||
duplicateRecursive(space, space.position, duplicatedParent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> _getAllTagValues(List<SpaceModel> spaces) {
|
|
||||||
final List<String> allTags = [];
|
|
||||||
for (final space in spaces) {
|
|
||||||
if (space.tags != null) {
|
|
||||||
allTags.addAll(space.listAllTagValues());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allTags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/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';
|
||||||
@ -10,25 +9,16 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
|
|||||||
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/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/create_subspace/views/create_subspace_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.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';
|
||||||
|
|
||||||
class CreateSpaceDialog extends StatefulWidget {
|
class CreateSpaceDialog extends StatefulWidget {
|
||||||
final Function(
|
final Function(String, String, List<SelectedProduct> selectedProducts,
|
||||||
String,
|
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) onCreateSpace;
|
||||||
String,
|
|
||||||
List<SelectedProduct> selectedProducts,
|
|
||||||
SpaceTemplateModel? spaceModel,
|
|
||||||
List<SubspaceModel>? subspaces,
|
|
||||||
List<Tag>? tags) onCreateSpace;
|
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final String? name;
|
final String? name;
|
||||||
final String? icon;
|
final String? icon;
|
||||||
@ -39,8 +29,6 @@ class CreateSpaceDialog extends StatefulWidget {
|
|||||||
final List<SpaceTemplateModel>? spaceModels;
|
final List<SpaceTemplateModel>? spaceModels;
|
||||||
final List<SubspaceModel>? subspaces;
|
final List<SubspaceModel>? subspaces;
|
||||||
final List<Tag>? tags;
|
final List<Tag>? tags;
|
||||||
final List<String>? allTags;
|
|
||||||
final SpaceTemplateModel? currentSpaceModel;
|
|
||||||
|
|
||||||
const CreateSpaceDialog(
|
const CreateSpaceDialog(
|
||||||
{super.key,
|
{super.key,
|
||||||
@ -51,12 +39,10 @@ class CreateSpaceDialog extends StatefulWidget {
|
|||||||
this.icon,
|
this.icon,
|
||||||
this.isEdit = false,
|
this.isEdit = false,
|
||||||
this.editSpace,
|
this.editSpace,
|
||||||
this.allTags,
|
|
||||||
this.selectedProducts = const [],
|
this.selectedProducts = const [],
|
||||||
this.spaceModels,
|
this.spaceModels,
|
||||||
this.subspaces,
|
this.subspaces,
|
||||||
this.tags,
|
this.tags});
|
||||||
this.currentSpaceModel});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||||
@ -83,9 +69,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||||
isOkButtonEnabled =
|
isOkButtonEnabled =
|
||||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||||
tags = widget.tags ?? [];
|
|
||||||
subspaces = widget.subspaces ?? [];
|
|
||||||
selectedSpaceModel = widget.currentSpaceModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -156,9 +139,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
TextField(
|
||||||
width: screenWidth * 0.25,
|
|
||||||
child: TextField(
|
|
||||||
controller: nameController,
|
controller: nameController,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
enteredName = value.trim();
|
enteredName = value.trim();
|
||||||
@ -178,13 +159,14 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: const TextStyle(color: Colors.black),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Please enter the name',
|
hintText: 'Please enter the name',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: const TextStyle(
|
||||||
.textTheme
|
fontSize: 14,
|
||||||
.bodyMedium!
|
color: ColorsManager.lightGrayColor,
|
||||||
.copyWith(color: ColorsManager.lightGrayColor),
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: ColorsManager.boxColor,
|
fillColor: ColorsManager.boxColor,
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
@ -205,7 +187,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
if (isNameFieldInvalid)
|
if (isNameFieldInvalid)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
@ -230,20 +211,46 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
selectedSpaceModel == null
|
selectedSpaceModel == null
|
||||||
? TextButton(
|
? DefaultButton(
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showLinkSpaceModelDialog(context);
|
_showLinkSpaceModelDialog(context);
|
||||||
},
|
},
|
||||||
child: const ButtonContentWidget(
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
svgAssets: Assets.link,
|
foregroundColor: Colors.black,
|
||||||
label: 'Link a space model',
|
borderColor: ColorsManager.neutralGray,
|
||||||
|
borderRadius: 16.0,
|
||||||
|
padding: 10.0, // Reduced padding for smaller size
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 6.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.link,
|
||||||
|
width: screenWidth *
|
||||||
|
0.015, // Adjust icon size
|
||||||
|
height: screenWidth * 0.015,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'Link a space model',
|
||||||
|
overflow: TextOverflow
|
||||||
|
.ellipsis, // Prevent overflow
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(
|
: Container(
|
||||||
width: screenWidth * 0.25,
|
width: screenWidth * 0.35,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 10.0, horizontal: 16.0),
|
vertical: 10.0, horizontal: 16.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -257,10 +264,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
Chip(
|
Chip(
|
||||||
label: Text(
|
label: Text(
|
||||||
selectedSpaceModel?.modelName ?? '',
|
selectedSpaceModel?.modelName ?? '',
|
||||||
style: Theme.of(context)
|
style: const TextStyle(
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.spaceColor),
|
color: ColorsManager.spaceColor),
|
||||||
),
|
),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
@ -294,25 +298,25 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
Row(
|
const Row(
|
||||||
children: [
|
children: [
|
||||||
const Expanded(
|
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: EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
'OR',
|
'OR',
|
||||||
style: Theme.of(context)
|
style: TextStyle(
|
||||||
.textTheme
|
color: Colors.black,
|
||||||
.bodyMedium
|
fontWeight: FontWeight.bold,
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Expanded(
|
),
|
||||||
|
Expanded(
|
||||||
child: Divider(
|
child: Divider(
|
||||||
color: ColorsManager.neutralGray,
|
color: ColorsManager.neutralGray,
|
||||||
thickness: 1.0,
|
thickness: 1.0,
|
||||||
@ -321,23 +325,48 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
subspaces == null || subspaces!.isEmpty
|
subspaces == null
|
||||||
? TextButton(
|
? DefaultButton(
|
||||||
style: TextButton.styleFrom(
|
onPressed: () {
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
overlayColor: ColorsManager.transparentColor,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
_showSubSpaceDialog(context, enteredName, [],
|
_showSubSpaceDialog(context, enteredName, [],
|
||||||
false, widget.products, subspaces);
|
false, widget.products, subspaces);
|
||||||
},
|
},
|
||||||
child: const ButtonContentWidget(
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
icon: Icons.add,
|
foregroundColor: Colors.black,
|
||||||
label: 'Create Sub Space',
|
borderColor: ColorsManager.neutralGray,
|
||||||
|
borderRadius: 16.0,
|
||||||
|
padding: 10.0, // Reduced padding for smaller size
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 6.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.addIcon,
|
||||||
|
width: screenWidth *
|
||||||
|
0.015, // Adjust icon size
|
||||||
|
height: screenWidth * 0.015,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 3),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
'Create sub space',
|
||||||
|
overflow: TextOverflow
|
||||||
|
.ellipsis, // Prevent overflow
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: SizedBox(
|
: SizedBox(
|
||||||
width: screenWidth * 0.25,
|
width: screenWidth * 0.35,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -353,46 +382,50 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
if (subspaces != null)
|
if (subspaces != null)
|
||||||
...subspaces!.map((subspace) {
|
...subspaces!.map(
|
||||||
return Column(
|
(subspace) => Chip(
|
||||||
crossAxisAlignment:
|
label: Text(
|
||||||
CrossAxisAlignment.start,
|
subspace.subspaceName,
|
||||||
children: [
|
style: const TextStyle(
|
||||||
SubspaceNameDisplayWidget(
|
color: ColorsManager
|
||||||
text: subspace.subspaceName,
|
.spaceColor), // Text color
|
||||||
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;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
backgroundColor: ColorsManager
|
||||||
);
|
.whiteColors, // Chip background color
|
||||||
}),
|
shape: RoundedRectangleBorder(
|
||||||
EditChip(
|
borderRadius: BorderRadius.circular(
|
||||||
|
16), // Rounded chip
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager
|
||||||
|
.spaceColor), // Border color
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
_showSubSpaceDialog(context, enteredName,
|
_showSubSpaceDialog(
|
||||||
[], true, widget.products, subspaces);
|
context,
|
||||||
|
enteredName,
|
||||||
|
[],
|
||||||
|
false,
|
||||||
|
widget.products,
|
||||||
|
subspaces);
|
||||||
},
|
},
|
||||||
)
|
child: Chip(
|
||||||
|
label: const Text(
|
||||||
|
'Edit',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor:
|
||||||
|
ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -419,7 +452,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
// Combine tags from spaceModel and subspaces
|
// Combine tags from spaceModel and subspaces
|
||||||
...TagHelper.groupTags([
|
..._groupTags([
|
||||||
...?tags,
|
...?tags,
|
||||||
...?subspaces?.expand(
|
...?subspaces?.expand(
|
||||||
(subspace) => subspace.tags ?? [])
|
(subspace) => subspace.tags ?? [])
|
||||||
@ -436,12 +469,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
'x${entry.value}', // Show count
|
'x${entry.value}', // Show count
|
||||||
style: Theme.of(context)
|
style: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.spaceColor,
|
||||||
.bodySmall
|
),
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager
|
|
||||||
.spaceColor),
|
|
||||||
),
|
),
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
ColorsManager.whiteColors,
|
ColorsManager.whiteColors,
|
||||||
@ -454,52 +484,70 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
GestureDetector(
|
||||||
EditChip(onTap: () async {
|
onTap: () async {
|
||||||
final result = await showDialog(
|
_showTagCreateDialog(context, enteredName,
|
||||||
context: context,
|
widget.products);
|
||||||
builder: (context) => AssignTagDialog(
|
// Edit action
|
||||||
products: widget.products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
addedProducts: TagHelper
|
|
||||||
.createInitialSelectedProductsForTags(
|
|
||||||
tags ?? [], subspaces),
|
|
||||||
title: 'Edit Device',
|
|
||||||
initialTags:
|
|
||||||
TagHelper.generateInitialForTags(
|
|
||||||
spaceTags: tags,
|
|
||||||
subspaces: subspaces),
|
|
||||||
spaceName: widget.name ?? '',
|
|
||||||
onSave:
|
|
||||||
(updatedTags, updatedSubspaces) {
|
|
||||||
setState(() {
|
|
||||||
tags = updatedTags;
|
|
||||||
subspaces = updatedSubspaces;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
child: Chip(
|
||||||
|
label: const Text(
|
||||||
|
'Edit',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor:
|
||||||
|
ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: TextButton(
|
: DefaultButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showTagCreateDialog(
|
_showTagCreateDialog(
|
||||||
context,
|
context, enteredName, widget.products);
|
||||||
enteredName,
|
|
||||||
widget.isEdit,
|
|
||||||
widget.products,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
padding: EdgeInsets.zero,
|
foregroundColor: Colors.black,
|
||||||
|
borderColor: ColorsManager.neutralGray,
|
||||||
|
borderRadius: 16.0,
|
||||||
|
padding: 10.0, // Reduced padding for smaller size
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(left: 6.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.addIcon,
|
||||||
|
width: screenWidth *
|
||||||
|
0.015, // Adjust icon size
|
||||||
|
height: screenWidth * 0.015,
|
||||||
),
|
),
|
||||||
child: const ButtonContentWidget(
|
),
|
||||||
icon: Icons.add,
|
const SizedBox(width: 3),
|
||||||
label: 'Add Devices',
|
Flexible(
|
||||||
))
|
child: Text(
|
||||||
|
'Add devices',
|
||||||
|
overflow: TextOverflow
|
||||||
|
.ellipsis, // Prevent overflow
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -531,13 +579,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
? enteredName
|
? enteredName
|
||||||
: (widget.name ?? '');
|
: (widget.name ?? '');
|
||||||
if (newName.isNotEmpty) {
|
if (newName.isNotEmpty) {
|
||||||
widget.onCreateSpace(
|
widget.onCreateSpace(newName, selectedIcon,
|
||||||
newName,
|
selectedProducts, selectedSpaceModel,subspaces,tags);
|
||||||
selectedIcon,
|
|
||||||
selectedProducts,
|
|
||||||
selectedSpaceModel,
|
|
||||||
subspaces,
|
|
||||||
tags);
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -573,23 +616,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _isNameConflict(String value) {
|
bool _isNameConflict(String value) {
|
||||||
final parentSpace = widget.parentSpace;
|
return (widget.parentSpace?.children.any((child) => child.name == value) ??
|
||||||
final editSpace = widget.editSpace;
|
false) ||
|
||||||
final siblings = parentSpace?.children
|
(widget.parentSpace?.name == value) ||
|
||||||
.where((child) => child.uuid != editSpace?.uuid)
|
(widget.editSpace?.parent?.name == value) ||
|
||||||
.toList() ??
|
(widget.editSpace?.children.any((child) => child.name == value) ??
|
||||||
[];
|
false);
|
||||||
final siblingConflict = siblings.any((child) => child.name == value);
|
|
||||||
final parentConflict =
|
|
||||||
parentSpace?.name == value && parentSpace?.uuid != editSpace?.uuid;
|
|
||||||
final parentOfEditSpaceConflict = editSpace?.parent?.name == value &&
|
|
||||||
editSpace?.parent?.uuid != editSpace?.uuid;
|
|
||||||
final childConflict =
|
|
||||||
editSpace?.children.any((child) => child.name == value) ?? false;
|
|
||||||
return siblingConflict ||
|
|
||||||
parentConflict ||
|
|
||||||
parentOfEditSpaceConflict ||
|
|
||||||
childConflict;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showLinkSpaceModelDialog(BuildContext context) {
|
void _showLinkSpaceModelDialog(BuildContext context) {
|
||||||
@ -603,7 +635,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
selectedSpaceModel = selectedModel;
|
selectedSpaceModel = selectedModel;
|
||||||
subspaces = null;
|
subspaces = null;
|
||||||
tags = null;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -624,32 +655,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CreateSubSpaceDialog(
|
return CreateSubSpaceDialog(
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
dialogTitle: 'Create Sub-space',
|
||||||
spaceTags: spaceTags,
|
spaceTags: spaceTags,
|
||||||
isEdit: isEdit,
|
isEdit: isEdit,
|
||||||
products: products,
|
products: products,
|
||||||
existingSubSpaces: existingSubSpaces,
|
existingSubSpaces: existingSubSpaces,
|
||||||
onSave: (slectedSubspaces) {
|
onSave: (slectedSubspaces) {
|
||||||
final List<Tag> tagsToAppendToSpace = [];
|
|
||||||
|
|
||||||
if (slectedSubspaces != null) {
|
|
||||||
final updatedIds =
|
|
||||||
slectedSubspaces.map((s) => s.internalId).toSet();
|
|
||||||
if (existingSubSpaces != null) {
|
|
||||||
final deletedSubspaces = existingSubSpaces
|
|
||||||
.where((s) => !updatedIds.contains(s.internalId))
|
|
||||||
.toList();
|
|
||||||
for (var s in deletedSubspaces) {
|
|
||||||
if (s.tags != null) {
|
|
||||||
tagsToAppendToSpace.addAll(s.tags!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (slectedSubspaces != null) {
|
if (slectedSubspaces != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
subspaces = slectedSubspaces;
|
subspaces = slectedSubspaces;
|
||||||
tags?.addAll(tagsToAppendToSpace);
|
|
||||||
selectedSpaceModel = null;
|
selectedSpaceModel = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -658,43 +672,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
void _showTagCreateDialog(
|
||||||
List<ProductModel>? products) {
|
BuildContext context, String name, List<ProductModel>? products) {
|
||||||
isEdit
|
showDialog(
|
||||||
? showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AssignTagDialog(
|
|
||||||
title: 'Edit Device',
|
|
||||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
|
||||||
tags, subspaces),
|
|
||||||
spaceName: name,
|
|
||||||
products: products,
|
|
||||||
subspaces: subspaces,
|
|
||||||
allTags: widget.allTags,
|
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
|
||||||
setState(() {
|
|
||||||
tags = selectedSpaceTags;
|
|
||||||
selectedSpaceModel = null;
|
|
||||||
|
|
||||||
if (selectedSubspaces != null) {
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (final subspace in subspaces!) {
|
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
|
||||||
if (subspace.subspaceName ==
|
|
||||||
selectedSubspace.subspaceName) {
|
|
||||||
subspace.tags = selectedSubspace.tags;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: showDialog(
|
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AddDeviceTypeWidget(
|
return AddDeviceTypeWidget(
|
||||||
@ -702,11 +682,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
spaceTags: tags,
|
spaceTags: tags,
|
||||||
isCreate: true,
|
allTags: [],
|
||||||
allTags: widget.allTags,
|
|
||||||
initialSelectedProducts:
|
initialSelectedProducts:
|
||||||
TagHelper.createInitialSelectedProductsForTags(
|
createInitialSelectedProducts(tags, subspaces),
|
||||||
tags, subspaces),
|
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = selectedSpaceTags;
|
tags = selectedSpaceTags;
|
||||||
@ -730,4 +708,49 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<SelectedProduct> createInitialSelectedProducts(
|
||||||
|
List<Tag>? tags, List<SubspaceModel>? subspaces) {
|
||||||
|
final Map<ProductModel, int> productCounts = {};
|
||||||
|
|
||||||
|
if (tags != null) {
|
||||||
|
for (var tag in tags) {
|
||||||
|
if (tag.product != null) {
|
||||||
|
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subspaces != null) {
|
||||||
|
for (var subspace in subspaces) {
|
||||||
|
if (subspace.tags != null) {
|
||||||
|
for (var tag in subspace.tags!) {
|
||||||
|
if (tag.product != null) {
|
||||||
|
productCounts[tag.product!] =
|
||||||
|
(productCounts[tag.product!] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return productCounts.entries
|
||||||
|
.map((entry) => SelectedProduct(
|
||||||
|
productId: entry.key.uuid,
|
||||||
|
count: entry.value,
|
||||||
|
productName: entry.key.name ?? 'Unnamed',
|
||||||
|
product: entry.key,
|
||||||
|
))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<ProductModel, int> _groupTags(List<Tag> tags) {
|
||||||
|
final Map<ProductModel, int> groupedTags = {};
|
||||||
|
for (var tag in tags) {
|
||||||
|
if (tag.product != null) {
|
||||||
|
groupedTags[tag.product!] = (groupedTags[tag.product!] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groupedTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class DuplicateProcessDialog extends StatefulWidget {
|
|
||||||
final VoidCallback onDuplicate;
|
|
||||||
|
|
||||||
const DuplicateProcessDialog({required this.onDuplicate, Key? key})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_DuplicateProcessDialogState createState() => _DuplicateProcessDialogState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DuplicateProcessDialogState extends State<DuplicateProcessDialog> {
|
|
||||||
bool isDuplicating = true;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_startDuplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _startDuplication() async {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
widget.onDuplicate();
|
|
||||||
});
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
setState(() {
|
|
||||||
isDuplicating = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
if (mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
|
||||||
|
|
||||||
return AlertDialog(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
content: SizedBox(
|
|
||||||
width: screenWidth * 0.4,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (isDuplicating) ...[
|
|
||||||
const CircularProgressIndicator(
|
|
||||||
color: ColorsManager.vividBlue,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
Text(
|
|
||||||
"Duplicating in progress",
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headlineSmall
|
|
||||||
?.copyWith(color: ColorsManager.primaryColor),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
const Icon(
|
|
||||||
Icons.check_circle,
|
|
||||||
color: ColorsManager.vividBlue,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
Text(
|
|
||||||
"Duplicating successful",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headlineSmall
|
|
||||||
?.copyWith(color: ColorsManager.primaryColor),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/space_tem
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/view/space_model_page.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/view/space_model_page.dart';
|
||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||||
|
|
||||||
class LoadedSpaceView extends StatefulWidget {
|
class LoadedSpaceView extends StatelessWidget {
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
final CommunityModel? selectedCommunity;
|
final CommunityModel? selectedCommunity;
|
||||||
final SpaceModel? selectedSpace;
|
final SpaceModel? selectedSpace;
|
||||||
@ -26,73 +26,41 @@ class LoadedSpaceView extends StatefulWidget {
|
|||||||
this.selectedSpace,
|
this.selectedSpace,
|
||||||
this.products,
|
this.products,
|
||||||
this.spaceModels,
|
this.spaceModels,
|
||||||
required this.shouldNavigateToSpaceModelPage,
|
required this.shouldNavigateToSpaceModelPage
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
_LoadedSpaceViewState createState() => _LoadedSpaceViewState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|
||||||
late List<SpaceTemplateModel> _spaceModels;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_spaceModels = List.from(widget.spaceModels ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(covariant LoadedSpaceView oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (widget.spaceModels != oldWidget.spaceModels) {
|
|
||||||
setState(() {
|
|
||||||
_spaceModels = List.from(widget.spaceModels ?? []);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSpaceModelsUpdated(List<SpaceTemplateModel> updatedModels) {
|
|
||||||
if (mounted && updatedModels != _spaceModels) {
|
|
||||||
setState(() {
|
|
||||||
_spaceModels = updatedModels;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SidebarWidget(
|
SidebarWidget(
|
||||||
communities: widget.communities,
|
communities: communities,
|
||||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
selectedSpaceUuid:
|
||||||
widget.selectedCommunity?.uuid ??
|
selectedSpace?.uuid ?? selectedCommunity?.uuid ?? '',
|
||||||
'',
|
|
||||||
),
|
),
|
||||||
widget.shouldNavigateToSpaceModelPage
|
shouldNavigateToSpaceModelPage
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (context) => SpaceModelBloc(
|
create: (context) => SpaceModelBloc(
|
||||||
api: SpaceModelManagementApi(),
|
api: SpaceModelManagementApi(),
|
||||||
initialSpaceModels: _spaceModels,
|
initialSpaceModels: spaceModels ?? [],
|
||||||
),
|
),
|
||||||
child: SpaceModelPage(
|
child: SpaceModelPage(
|
||||||
products: widget.products,
|
products: products,
|
||||||
onSpaceModelsUpdated: _onSpaceModelsUpdated,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: CommunityStructureArea(
|
: CommunityStructureArea(
|
||||||
selectedCommunity: widget.selectedCommunity,
|
selectedCommunity: selectedCommunity,
|
||||||
selectedSpace: widget.selectedSpace,
|
selectedSpace: selectedSpace,
|
||||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
spaces: selectedCommunity?.spaces ?? [],
|
||||||
products: widget.products,
|
products: products,
|
||||||
communities: widget.communities,
|
communities: communities,
|
||||||
spaceModels: _spaceModels,
|
spaceModels: spaceModels,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -100,4 +68,13 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpaceModel? findSpaceByUuid(String? uuid, List<CommunityModel> communities) {
|
||||||
|
for (var community in communities) {
|
||||||
|
for (var space in community.spaces) {
|
||||||
|
if (space.uuid == uuid) return space;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,11 +199,9 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
|||||||
},
|
},
|
||||||
children: hasChildren
|
children: hasChildren
|
||||||
? community.spaces
|
? community.spaces
|
||||||
.where((space) => (space.status != SpaceStatus.deleted ||
|
|
||||||
space.status != SpaceStatus.parentDeleted))
|
|
||||||
.map((space) => _buildSpaceTile(space, community))
|
.map((space) => _buildSpaceTile(space, community))
|
||||||
.toList()
|
.toList()
|
||||||
: null,
|
: null, // Render spaces within the community
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,8 @@ class SpaceWidget extends StatelessWidget {
|
|||||||
top: position.dy,
|
top: position.dy,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child:
|
||||||
|
Container(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
@ -38,10 +39,11 @@ class SpaceWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Icons.location_on, color: ColorsManager.spaceColor),
|
const Icon(Icons.location_on, color: ColorsManager.spaceColor),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(name, style: Theme.of(context).textTheme.bodyMedium),
|
Text(name, style: const TextStyle(fontSize: 16)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: allTags,
|
tags: allTags,
|
||||||
isSaveEnabled: _validateTags(allTags),
|
isSaveEnabled: _validateTags(allTags),
|
||||||
errorMessage: ''));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
on<UpdateTagEvent>((event, emit) {
|
on<UpdateTagEvent>((event, emit) {
|
||||||
@ -117,10 +117,12 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _validateTags(List<Tag> tags) {
|
bool _validateTags(List<Tag> tags) {
|
||||||
|
if (tags.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
||||||
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
return uniqueTags.length == tags.length && !hasEmptyTag;
|
||||||
return isValid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _getValidationError(List<Tag> tags) {
|
String? _getValidationError(List<Tag> tags) {
|
||||||
|
@ -1,10 +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/common/dialog_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/common/dialog_textfield_dropdown.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/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,7 +9,6 @@ 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/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class AssignTagDialog extends StatelessWidget {
|
class AssignTagDialog extends StatelessWidget {
|
||||||
@ -41,11 +37,8 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<String> locations = (subspaces ?? [])
|
final List<String> locations =
|
||||||
.map((subspace) => subspace.subspaceName)
|
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList();
|
||||||
.toList()
|
|
||||||
..add('Main Space');
|
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagBloc()
|
create: (_) => AssignTagBloc()
|
||||||
..add(InitializeTags(
|
..add(InitializeTags(
|
||||||
@ -86,7 +79,6 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
Theme.of(context).textTheme.bodyMedium)),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
numeric: false,
|
|
||||||
label: Text('Tag',
|
label: Text('Tag',
|
||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
Theme.of(context).textTheme.bodyMedium)),
|
||||||
@ -97,32 +89,30 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
rows: state.tags.isEmpty
|
rows: state.tags.isEmpty
|
||||||
? [
|
? [
|
||||||
DataRow(cells: [
|
const DataRow(cells: [
|
||||||
DataCell(
|
DataCell(
|
||||||
Center(
|
Center(
|
||||||
child: Text('No Data Available',
|
child: Text(
|
||||||
style: Theme.of(context)
|
'No Data Available',
|
||||||
.textTheme
|
style: TextStyle(
|
||||||
.bodyMedium
|
fontSize: 14,
|
||||||
?.copyWith(
|
color: ColorsManager.lightGrayColor,
|
||||||
color: ColorsManager
|
|
||||||
.lightGrayColor,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const DataCell(SizedBox()),
|
),
|
||||||
const DataCell(SizedBox()),
|
),
|
||||||
const DataCell(SizedBox()),
|
DataCell(SizedBox()),
|
||||||
|
DataCell(SizedBox()),
|
||||||
|
DataCell(SizedBox()),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
: List.generate(state.tags.length, (index) {
|
: List.generate(state.tags.length, (index) {
|
||||||
final tag = state.tags[index];
|
final tag = state.tags[index];
|
||||||
final controller = controllers[index];
|
final controller = controllers[index];
|
||||||
final availableTags = getAvailableTags(
|
|
||||||
allTags ?? [], state.tags, tag);
|
|
||||||
return DataRow(
|
return DataRow(
|
||||||
cells: [
|
cells: [
|
||||||
DataCell(Text((index + 1).toString())),
|
DataCell(Text(index.toString())),
|
||||||
DataCell(
|
DataCell(
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
@ -133,53 +123,28 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
tag.product?.name ?? 'Unknown',
|
tag.product?.name ?? 'Unknown',
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)),
|
)),
|
||||||
const SizedBox(width: 10),
|
IconButton(
|
||||||
Container(
|
icon: const Icon(Icons.close,
|
||||||
width: 20.0,
|
color: ColorsManager.warningRed,
|
||||||
height: 20.0,
|
size: 16),
|
||||||
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: () {
|
onPressed: () {
|
||||||
context
|
context.read<AssignTagBloc>().add(
|
||||||
.read<AssignTagBloc>()
|
DeleteTag(
|
||||||
.add(DeleteTag(
|
|
||||||
tagToDelete: tag,
|
tagToDelete: tag,
|
||||||
tags: state.tags));
|
tags: state.tags));
|
||||||
},
|
},
|
||||||
tooltip: 'Delete Tag',
|
tooltip: 'Delete Tag',
|
||||||
padding: EdgeInsets.zero,
|
)
|
||||||
constraints:
|
|
||||||
const BoxConstraints(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
Container(
|
Row(
|
||||||
alignment: Alignment
|
children: [
|
||||||
.centerLeft, // Align cell content to the left
|
Expanded(
|
||||||
child: SizedBox(
|
child: TextFormField(
|
||||||
width: double
|
controller: controller,
|
||||||
.infinity, // Ensure full width for dropdown
|
onChanged: (value) {
|
||||||
child: DialogTextfieldDropdown(
|
|
||||||
items: availableTags,
|
|
||||||
initialValue: tag.tag,
|
|
||||||
onSelected: (value) {
|
|
||||||
controller.text = value;
|
|
||||||
context
|
context
|
||||||
.read<AssignTagBloc>()
|
.read<AssignTagBloc>()
|
||||||
.add(UpdateTagEvent(
|
.add(UpdateTagEvent(
|
||||||
@ -187,26 +152,118 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
tag: value.trim(),
|
tag: value.trim(),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Enter Tag',
|
||||||
|
border: InputBorder.none,
|
||||||
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context)
|
||||||
|
.size
|
||||||
|
.width *
|
||||||
|
0.15,
|
||||||
|
child: PopupMenuButton<String>(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.arrow_drop_down,
|
||||||
|
color:
|
||||||
|
ColorsManager.blackColor),
|
||||||
|
onSelected: (value) {
|
||||||
|
controller.text = value;
|
||||||
|
context
|
||||||
|
.read<AssignTagBloc>()
|
||||||
|
.add(UpdateTagEvent(
|
||||||
|
index: index,
|
||||||
|
tag: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
itemBuilder: (context) {
|
||||||
|
return (allTags ?? [])
|
||||||
|
.where((tagValue) => !state
|
||||||
|
.tags
|
||||||
|
.map((e) => e.tag)
|
||||||
|
.contains(tagValue))
|
||||||
|
.map((tagValue) {
|
||||||
|
return PopupMenuItem<String>(
|
||||||
|
textStyle: const TextStyle(
|
||||||
|
color: ColorsManager
|
||||||
|
.textPrimaryColor),
|
||||||
|
value: tagValue,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints:
|
||||||
|
BoxConstraints(
|
||||||
|
minWidth: MediaQuery.of(
|
||||||
|
context)
|
||||||
|
.size
|
||||||
|
.width *
|
||||||
|
0.15,
|
||||||
|
maxWidth: MediaQuery.of(
|
||||||
|
context)
|
||||||
|
.size
|
||||||
|
.width *
|
||||||
|
0.15,
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
tagValue,
|
||||||
|
overflow: TextOverflow
|
||||||
|
.ellipsis,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}).toList();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
SizedBox(
|
DropdownButtonHideUnderline(
|
||||||
width: double.infinity,
|
child: DropdownButton<String>(
|
||||||
child: DialogDropdown(
|
value: tag.location ?? 'Main',
|
||||||
items: locations,
|
dropdownColor: ColorsManager
|
||||||
selectedValue:
|
.whiteColors, // Dropdown background
|
||||||
tag.location ?? 'Main Space',
|
style: const TextStyle(
|
||||||
onSelected: (value) {
|
color: Colors
|
||||||
|
.black), // Style for selected text
|
||||||
|
items: [
|
||||||
|
const DropdownMenuItem<String>(
|
||||||
|
value: 'Main Space',
|
||||||
|
child: Text(
|
||||||
|
'Main Space',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager
|
||||||
|
.textPrimaryColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
...locations.map((location) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: location,
|
||||||
|
child: Text(
|
||||||
|
location,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: ColorsManager
|
||||||
|
.textPrimaryColor),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
],
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
context
|
context
|
||||||
.read<AssignTagBloc>()
|
.read<AssignTagBloc>()
|
||||||
.add(UpdateLocation(
|
.add(UpdateLocation(
|
||||||
index: index,
|
index: index,
|
||||||
location: value,
|
location: value,
|
||||||
));
|
));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -214,11 +271,10 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage!,
|
||||||
.textTheme
|
style: const TextStyle(color: ColorsManager.warningRed),
|
||||||
.bodySmall
|
),
|
||||||
?.copyWith(color: ColorsManager.warningRed)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -228,39 +284,13 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Builder(
|
child: CancelButton(
|
||||||
builder: (buttonContext) => CancelButton(
|
label: 'Cancel',
|
||||||
label: 'Add New Device',
|
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
|
||||||
final result =
|
|
||||||
TagHelper.processTags(updatedTags, subspaces);
|
|
||||||
|
|
||||||
final processedTags =
|
|
||||||
result['updatedTags'] as List<Tag>;
|
|
||||||
final processedSubspaces = List<SubspaceModel>.from(
|
|
||||||
result['subspaces'] as List<dynamic>);
|
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AddDeviceTypeWidget(
|
|
||||||
products: products,
|
|
||||||
subspaces: processedSubspaces,
|
|
||||||
initialSelectedProducts: TagHelper
|
|
||||||
.createInitialSelectedProductsForTags(
|
|
||||||
processedTags, processedSubspaces),
|
|
||||||
spaceName: spaceName,
|
|
||||||
spaceTags: processedTags,
|
|
||||||
isCreate: false,
|
|
||||||
onSave: onSave,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
@ -271,17 +301,23 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
onPressed: state.isSaveEnabled
|
onPressed: state.isSaveEnabled
|
||||||
? () async {
|
? () 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();
|
Navigator.of(context).pop();
|
||||||
|
final assignedTags = <Tag>{};
|
||||||
|
for (var tag in state.tags) {
|
||||||
|
if (tag.location == null ||
|
||||||
|
subspaces == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (var subspace in subspaces!) {
|
||||||
|
if (tag.location == subspace.subspaceName) {
|
||||||
|
subspace.tags ??= [];
|
||||||
|
subspace.tags!.add(tag);
|
||||||
|
assignedTags.add(tag);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSave!(state.tags,subspaces);
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: const Text('Save'),
|
child: const Text('Save'),
|
||||||
@ -301,15 +337,4 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getAvailableTags(
|
|
||||||
List<String> allTags, List<Tag> currentTags, Tag currentTag) {
|
|
||||||
List<String> availableTagsForTagModel = TagHelper.getAvailableTags<Tag>(
|
|
||||||
allTags: allTags,
|
|
||||||
currentTags: currentTags,
|
|
||||||
currentTag: currentTag,
|
|
||||||
getTag: (tag) => tag.tag ?? '',
|
|
||||||
);
|
|
||||||
return availableTagsForTagModel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model
|
|||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.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/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';
|
|
||||||
|
|
||||||
class AssignTagModelsDialog extends StatelessWidget {
|
class AssignTagModelsDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -30,7 +29,6 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
final String title;
|
final String title;
|
||||||
final BuildContext? pageContext;
|
final BuildContext? pageContext;
|
||||||
final List<String>? otherSpaceModels;
|
final List<String>? otherSpaceModels;
|
||||||
final List<SpaceTemplateModel>? allSpaceModels;
|
|
||||||
|
|
||||||
const AssignTagModelsDialog(
|
const AssignTagModelsDialog(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
@ -44,8 +42,7 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
required this.title,
|
required this.title,
|
||||||
this.pageContext,
|
this.pageContext,
|
||||||
this.otherSpaceModels,
|
this.otherSpaceModels,
|
||||||
this.spaceModel,
|
this.spaceModel})
|
||||||
this.allSpaceModels})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -112,29 +109,28 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
rows: state.tags.isEmpty
|
rows: state.tags.isEmpty
|
||||||
? [
|
? [
|
||||||
DataRow(cells: [
|
const DataRow(cells: [
|
||||||
DataCell(
|
DataCell(
|
||||||
Center(
|
Center(
|
||||||
child: Text('No Devices Available',
|
child: Text(
|
||||||
style: Theme.of(context)
|
'No Data Available',
|
||||||
.textTheme
|
style: TextStyle(
|
||||||
.bodyMedium
|
fontSize: 14,
|
||||||
?.copyWith(
|
color:
|
||||||
color: ColorsManager
|
ColorsManager.lightGrayColor,
|
||||||
.lightGrayColor,
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const DataCell(SizedBox()),
|
),
|
||||||
const DataCell(SizedBox()),
|
),
|
||||||
const DataCell(SizedBox()),
|
DataCell(SizedBox()),
|
||||||
|
DataCell(SizedBox()),
|
||||||
|
DataCell(SizedBox()),
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
: List.generate(state.tags.length, (index) {
|
: List.generate(state.tags.length, (index) {
|
||||||
final tag = state.tags[index];
|
final tag = state.tags[index];
|
||||||
final controller = controllers[index];
|
final controller = controllers[index];
|
||||||
final availableTags =
|
final availableTags = getAvailableTags(
|
||||||
TagHelper.getAvailableTagModels(
|
|
||||||
allTags ?? [], state.tags, tag);
|
allTags ?? [], state.tags, tag);
|
||||||
|
|
||||||
return DataRow(
|
return DataRow(
|
||||||
@ -215,8 +211,8 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: DialogDropdown(
|
child: DialogDropdown(
|
||||||
items: locations,
|
items: locations,
|
||||||
selectedValue: tag.location ??
|
selectedValue:
|
||||||
'Main Space',
|
tag.location ?? 'Main Space',
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
context
|
context
|
||||||
.read<
|
.read<
|
||||||
@ -234,11 +230,11 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage!,
|
||||||
.textTheme
|
style: const TextStyle(
|
||||||
.bodySmall
|
color: ColorsManager.warningRed),
|
||||||
?.copyWith(color: ColorsManager.warningRed)),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -252,18 +248,33 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
builder: (buttonContext) => CancelButton(
|
builder: (buttonContext) => CancelButton(
|
||||||
label: 'Add New Device',
|
label: 'Add New Device',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final updatedTags =
|
for (var tag in state.tags) {
|
||||||
List<TagModel>.from(state.tags);
|
if (tag.location == null ||
|
||||||
final result =
|
subspaces == null) {
|
||||||
TagHelper.updateSubspaceTagModels(
|
continue;
|
||||||
updatedTags, subspaces);
|
}
|
||||||
|
|
||||||
final processedTags =
|
final previousTagSubspace =
|
||||||
result['updatedTags'] as List<TagModel>;
|
checkTagExistInSubspace(
|
||||||
final processedSubspaces =
|
tag, subspaces ?? []);
|
||||||
List<SubspaceTemplateModel>.from(
|
|
||||||
result['subspaces'] as List<dynamic>);
|
|
||||||
|
|
||||||
|
if (tag.location == 'Main Space') {
|
||||||
|
removeTagFromSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
} else if (tag.location !=
|
||||||
|
previousTagSubspace?.subspaceName) {
|
||||||
|
removeTagFromSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
moveToNewSubspace(tag, subspaces ?? []);
|
||||||
|
state.tags.removeWhere(
|
||||||
|
(t) => t.internalId == tag.internalId);
|
||||||
|
} else {
|
||||||
|
updateTagInSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
state.tags.removeWhere(
|
||||||
|
(t) => t.internalId == tag.internalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
@ -273,25 +284,22 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
builder: (dialogContext) =>
|
builder: (dialogContext) =>
|
||||||
AddDeviceTypeModelWidget(
|
AddDeviceTypeModelWidget(
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: processedSubspaces,
|
subspaces: subspaces,
|
||||||
isCreate: false,
|
isCreate: false,
|
||||||
initialSelectedProducts: TagHelper
|
initialSelectedProducts:
|
||||||
.createInitialSelectedProducts(
|
addedProducts,
|
||||||
processedTags,
|
|
||||||
processedSubspaces),
|
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
spaceTagModels: processedTags,
|
spaceTagModels: state.tags,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
spaceModel: SpaceTemplateModel(
|
spaceModel: SpaceTemplateModel(
|
||||||
modelName: spaceName,
|
modelName: spaceName,
|
||||||
tags: updatedTags,
|
tags: state.tags,
|
||||||
uuid: spaceModel?.uuid,
|
uuid: spaceModel?.uuid,
|
||||||
internalId:
|
internalId:
|
||||||
spaceModel?.internalId,
|
spaceModel?.internalId,
|
||||||
subspaceModels:
|
subspaceModels: subspaces)),
|
||||||
processedSubspaces)),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -308,20 +316,33 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
onPressed: state.isSaveEnabled
|
onPressed: state.isSaveEnabled
|
||||||
? () async {
|
? () async {
|
||||||
final updatedTags =
|
for (var tag in state.tags) {
|
||||||
List<TagModel>.from(state.tags);
|
if (tag.location == null ||
|
||||||
|
subspaces == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
final result =
|
final previousTagSubspace =
|
||||||
TagHelper.updateSubspaceTagModels(
|
checkTagExistInSubspace(
|
||||||
updatedTags, subspaces);
|
tag, subspaces ?? []);
|
||||||
|
|
||||||
final processedTags =
|
|
||||||
result['updatedTags'] as List<TagModel>;
|
|
||||||
final processedSubspaces =
|
|
||||||
List<SubspaceTemplateModel>.from(
|
|
||||||
result['subspaces']
|
|
||||||
as List<dynamic>);
|
|
||||||
|
|
||||||
|
if (tag.location == 'Main Space') {
|
||||||
|
removeTagFromSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
} else if (tag.location !=
|
||||||
|
previousTagSubspace?.subspaceName) {
|
||||||
|
removeTagFromSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
moveToNewSubspace(tag, subspaces ?? []);
|
||||||
|
state.tags.removeWhere((t) =>
|
||||||
|
t.internalId == tag.internalId);
|
||||||
|
} else {
|
||||||
|
updateTagInSubspace(
|
||||||
|
tag, previousTagSubspace);
|
||||||
|
state.tags.removeWhere((t) =>
|
||||||
|
t.internalId == tag.internalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.popUntil((route) => route.isFirst);
|
.popUntil((route) => route.isFirst);
|
||||||
|
|
||||||
@ -330,18 +351,16 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
builder: (BuildContext dialogContext) {
|
builder: (BuildContext dialogContext) {
|
||||||
return CreateSpaceModelDialog(
|
return CreateSpaceModelDialog(
|
||||||
products: products,
|
products: products,
|
||||||
allSpaceModels: allSpaceModels,
|
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
spaceModel: SpaceTemplateModel(
|
spaceModel: SpaceTemplateModel(
|
||||||
modelName: spaceName,
|
modelName: spaceName,
|
||||||
tags: processedTags,
|
tags: state.tags,
|
||||||
uuid: spaceModel?.uuid,
|
uuid: spaceModel?.uuid,
|
||||||
internalId:
|
internalId:
|
||||||
spaceModel?.internalId,
|
spaceModel?.internalId,
|
||||||
subspaceModels:
|
subspaceModels: subspaces),
|
||||||
processedSubspaces),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -364,4 +383,50 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getAvailableTags(
|
||||||
|
List<String> allTags, List<TagModel> currentTags, TagModel currentTag) {
|
||||||
|
return allTags
|
||||||
|
.where((tagValue) => !currentTags
|
||||||
|
.where((e) => e != currentTag) // Exclude the current row
|
||||||
|
.map((e) => e.tag)
|
||||||
|
.contains(tagValue))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeTagFromSubspace(TagModel tag, SubspaceTemplateModel? subspace) {
|
||||||
|
subspace?.tags?.removeWhere((t) => t.internalId == tag.internalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubspaceTemplateModel? checkTagExistInSubspace(
|
||||||
|
TagModel tag, List<SubspaceTemplateModel>? subspaces) {
|
||||||
|
if (subspaces == null) return null;
|
||||||
|
for (var subspace in subspaces) {
|
||||||
|
if (subspace.tags == null) return null;
|
||||||
|
for (var t in subspace.tags!) {
|
||||||
|
if (tag.internalId == t.internalId) return subspace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveToNewSubspace(TagModel tag, List<SubspaceTemplateModel> subspaces) {
|
||||||
|
final targetSubspace = subspaces
|
||||||
|
.firstWhere((subspace) => subspace.subspaceName == tag.location);
|
||||||
|
|
||||||
|
targetSubspace.tags ??= [];
|
||||||
|
if (targetSubspace.tags?.any((t) => t.internalId == tag.internalId) !=
|
||||||
|
true) {
|
||||||
|
targetSubspace.tags?.add(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTagInSubspace(TagModel tag, SubspaceTemplateModel? subspace) {
|
||||||
|
final currentTag = subspace?.tags?.firstWhere(
|
||||||
|
(t) => t.internalId == tag.internalId,
|
||||||
|
);
|
||||||
|
if (currentTag != null) {
|
||||||
|
currentTag.tag = tag.tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,9 @@ class CreateCommunityDialog extends StatelessWidget {
|
|||||||
.read<CommunityDialogBloc>()
|
.read<CommunityDialogBloc>()
|
||||||
.add(ValidateCommunityNameEvent(value));
|
.add(ValidateCommunityNameEvent(value));
|
||||||
},
|
},
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
style: const TextStyle(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Please enter the community name',
|
hintText: 'Please enter the community name',
|
||||||
filled: true,
|
filled: true,
|
||||||
|
@ -6,43 +6,27 @@ import 'subspace_event.dart';
|
|||||||
import 'subspace_state.dart';
|
import 'subspace_state.dart';
|
||||||
|
|
||||||
class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||||
SubSpaceBloc() : super(SubSpaceState([], [], '', {})) {
|
SubSpaceBloc() : super(SubSpaceState([], [], '')) {
|
||||||
on<AddSubSpace>((event, emit) {
|
on<AddSubSpace>((event, emit) {
|
||||||
final existingNames = state.subSpaces.map((e) => e.subspaceName).toSet();
|
final existingNames =
|
||||||
|
state.subSpaces.map((e) => e.subspaceName).toSet();
|
||||||
|
|
||||||
if (existingNames.contains(event.subSpace.subspaceName.toLowerCase())) {
|
if (existingNames.contains(event.subSpace.subspaceName.toLowerCase())) {
|
||||||
final updatedDuplicates = Set<String>.from(state.duplicates)
|
|
||||||
..add(event.subSpace.subspaceName.toLowerCase());
|
|
||||||
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
|
||||||
..add(event.subSpace);
|
|
||||||
emit(SubSpaceState(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
state.subSpaces,
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
'*Duplicated sub-space name',
|
'Subspace name already exists.',
|
||||||
updatedDuplicates,
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
// Add subspace if no duplicate exists
|
|
||||||
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
||||||
..add(event.subSpace);
|
..add(event.subSpace);
|
||||||
|
|
||||||
if (state.duplicates.isNotEmpty) {
|
|
||||||
emit(SubSpaceState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
'*Duplicated sub-space name',
|
|
||||||
state.duplicates,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
emit(SubSpaceState(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
'',
|
'',
|
||||||
state.duplicates,
|
|
||||||
// Clear error message
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle RemoveSubSpace Event
|
// Handle RemoveSubSpace Event
|
||||||
@ -61,50 +45,13 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
final nameOccurrences = <String, int>{};
|
|
||||||
for (final subSpace in updatedSubSpaces) {
|
|
||||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
|
||||||
nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
final updatedDuplicates = nameOccurrences.entries
|
|
||||||
.where((entry) => entry.value > 1)
|
|
||||||
.map((entry) => entry.key)
|
|
||||||
.toSet();
|
|
||||||
final errorMessage =
|
|
||||||
updatedDuplicates.isNotEmpty ? '*Duplicated sub-space name' : '';
|
|
||||||
|
|
||||||
emit(SubSpaceState(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
updatedSubspaceModels,
|
updatedSubspaceModels,
|
||||||
errorMessage,
|
'', // Clear error message
|
||||||
updatedDuplicates,
|
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
on<UpdateSubSpace>((event, emit) {
|
// Handle UpdateSubSpace Event
|
||||||
final updatedSubSpaces = state.subSpaces.map((subSpace) {
|
|
||||||
if (subSpace.uuid == event.updatedSubSpace.uuid) {
|
|
||||||
return event.updatedSubSpace;
|
|
||||||
}
|
|
||||||
return subSpace;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
final updatedSubspaceModels = List<UpdateSubspaceModel>.from(
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
);
|
|
||||||
|
|
||||||
updatedSubspaceModels.add(UpdateSubspaceModel(
|
|
||||||
action: Action.update,
|
|
||||||
uuid: event.updatedSubSpace.uuid!,
|
|
||||||
));
|
|
||||||
|
|
||||||
emit(SubSpaceState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
updatedSubspaceModels,
|
|
||||||
'',
|
|
||||||
state.duplicates,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,26 +4,23 @@ class SubSpaceState {
|
|||||||
final List<SubspaceModel> subSpaces;
|
final List<SubspaceModel> subSpaces;
|
||||||
final List<UpdateSubspaceModel> updatedSubSpaceModels;
|
final List<UpdateSubspaceModel> updatedSubSpaceModels;
|
||||||
final String errorMessage;
|
final String errorMessage;
|
||||||
final Set<String> duplicates;
|
|
||||||
|
|
||||||
SubSpaceState(
|
SubSpaceState(
|
||||||
this.subSpaces,
|
this.subSpaces,
|
||||||
this.updatedSubSpaceModels,
|
this.updatedSubSpaceModels,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
this.duplicates,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
SubSpaceState copyWith({
|
SubSpaceState copyWith({
|
||||||
List<SubspaceModel>? subSpaces,
|
List<SubspaceModel>? subSpaces,
|
||||||
List<UpdateSubspaceModel>? updatedSubSpaceModels,
|
List<UpdateSubspaceModel>? updatedSubSpaceModels,
|
||||||
String? errorMessage,
|
String? errorMessage,
|
||||||
Set<String>? duplicates,
|
|
||||||
}) {
|
}) {
|
||||||
return SubSpaceState(
|
return SubSpaceState(
|
||||||
subSpaces ?? this.subSpaces,
|
subSpaces ?? this.subSpaces,
|
||||||
updatedSubSpaceModels ?? this.updatedSubSpaceModels,
|
updatedSubSpaceModels ?? this.updatedSubSpaceModels,
|
||||||
errorMessage ?? this.errorMessage,
|
errorMessage ?? this.errorMessage,
|
||||||
duplicates ?? this.duplicates,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,41 +81,18 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
...state.subSpaces.asMap().entries.map(
|
...state.subSpaces.map(
|
||||||
(entry) {
|
(subSpace) => Chip(
|
||||||
final index = entry.key;
|
label: Text(
|
||||||
final subSpace = entry.value;
|
subSpace.subspaceName,
|
||||||
|
style: const TextStyle(
|
||||||
final lowerName =
|
color: ColorsManager.spaceColor),
|
||||||
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 Chip(
|
|
||||||
label: Text(subSpace.subspaceName,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(
|
|
||||||
color:
|
|
||||||
ColorsManager.spaceColor)),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(8),
|
||||||
side: BorderSide(
|
side: const BorderSide(
|
||||||
color: isDuplicate
|
color: ColorsManager.transparentColor,
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
width: 0,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -138,8 +115,7 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
onDeleted: () => context
|
onDeleted: () => context
|
||||||
.read<SubSpaceBloc>()
|
.read<SubSpaceBloc>()
|
||||||
.add(RemoveSubSpace(subSpace)),
|
.add(RemoveSubSpace(subSpace)),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 200,
|
width: 200,
|
||||||
@ -150,12 +126,9 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
hintText: state.subSpaces.isEmpty
|
hintText: state.subSpaces.isEmpty
|
||||||
? 'Please enter the name'
|
? 'Please enter the name'
|
||||||
: null,
|
: null,
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.lightGrayColor),
|
||||||
.bodySmall
|
),
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager
|
|
||||||
.lightGrayColor)),
|
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
if (value.trim().isNotEmpty) {
|
if (value.trim().isNotEmpty) {
|
||||||
context.read<SubSpaceBloc>().add(
|
context.read<SubSpaceBloc>().add(
|
||||||
@ -165,22 +138,23 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
textController.clear();
|
textController.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style:
|
style: const TextStyle(
|
||||||
Theme.of(context).textTheme.bodyMedium),
|
color: ColorsManager.blackColor),
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.errorMessage.isNotEmpty)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(state.errorMessage,
|
child: Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage,
|
||||||
.textTheme
|
style: const TextStyle(
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.warningRed,
|
color: ColorsManager.warningRed,
|
||||||
)),
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
@ -188,17 +162,13 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: CancelButton(
|
child: CancelButton(
|
||||||
label: 'Cancel',
|
label: 'Cancel',
|
||||||
onPressed: () async {
|
onPressed: () async {},
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: (state.errorMessage.isNotEmpty)
|
onPressed: () async {
|
||||||
? null
|
|
||||||
: () async {
|
|
||||||
final subSpaces = context
|
final subSpaces = context
|
||||||
.read<SubSpaceBloc>()
|
.read<SubSpaceBloc>()
|
||||||
.state
|
.state
|
||||||
@ -208,9 +178,7 @@ class CreateSubSpaceDialog extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
foregroundColor: state.errorMessage.isNotEmpty
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
? ColorsManager.whiteColorsWithOpacity
|
|
||||||
: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -25,30 +25,22 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|||||||
updatedDuplicates,
|
updatedDuplicates,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
|
// Add subspace if no duplicate exists
|
||||||
final updatedSubSpaces =
|
final updatedSubSpaces =
|
||||||
List<SubspaceTemplateModel>.from(state.subSpaces)
|
List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||||
..add(event.subSpace);
|
..add(event.subSpace);
|
||||||
|
|
||||||
if (state.duplicates.isNotEmpty) {
|
|
||||||
emit(SubSpaceModelState(
|
|
||||||
updatedSubSpaces,
|
|
||||||
state.updatedSubSpaceModels,
|
|
||||||
'*Duplicated sub-space name',
|
|
||||||
state.duplicates,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
emit(SubSpaceModelState(
|
emit(SubSpaceModelState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
'',
|
'',
|
||||||
state.duplicates,
|
state.duplicates,
|
||||||
|
// Clear error message
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle RemoveSubSpaceModel Event
|
// Handle RemoveSubSpaceModel Event
|
||||||
|
|
||||||
on<RemoveSubSpaceModel>((event, emit) {
|
on<RemoveSubSpaceModel>((event, emit) {
|
||||||
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||||
..remove(event.subSpace);
|
..remove(event.subSpace);
|
||||||
@ -56,6 +48,16 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|||||||
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
);
|
);
|
||||||
|
final nameOccurrences = <String, int>{};
|
||||||
|
for (final subSpace in updatedSubSpaces) {
|
||||||
|
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||||
|
nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
final updatedDuplicates = nameOccurrences.entries
|
||||||
|
.where((entry) => entry.value > 1)
|
||||||
|
.map((entry) => entry.key)
|
||||||
|
.toSet();
|
||||||
|
|
||||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||||
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
||||||
@ -64,28 +66,12 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count occurrences of sub-space names to identify duplicates
|
|
||||||
final nameOccurrences = <String, int>{};
|
|
||||||
for (final subSpace in updatedSubSpaces) {
|
|
||||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
|
||||||
nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify duplicate names
|
|
||||||
final updatedDuplicates = nameOccurrences.entries
|
|
||||||
.where((entry) => entry.value > 1)
|
|
||||||
.map((entry) => entry.key)
|
|
||||||
.toSet();
|
|
||||||
|
|
||||||
// Determine the error message
|
|
||||||
final errorMessage =
|
|
||||||
updatedDuplicates.isNotEmpty ? '*Duplicated sub-space name' : '';
|
|
||||||
|
|
||||||
emit(SubSpaceModelState(
|
emit(SubSpaceModelState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
updatedSubspaceModels,
|
updatedSubspaceModels,
|
||||||
errorMessage,
|
'',
|
||||||
updatedDuplicates,
|
updatedDuplicates,
|
||||||
|
// Clear error message
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -94,13 +94,12 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
|
|||||||
duplicateIndices.indexOf(index) != 0;
|
duplicateIndices.indexOf(index) != 0;
|
||||||
|
|
||||||
return Chip(
|
return Chip(
|
||||||
label: Text(subSpace.subspaceName,
|
label: Text(
|
||||||
style: Theme.of(context)
|
subSpace.subspaceName,
|
||||||
.textTheme
|
style: const TextStyle(
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.spaceColor,
|
color: ColorsManager.spaceColor,
|
||||||
)),
|
),
|
||||||
|
),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
@ -142,12 +141,9 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
|
|||||||
hintText: state.subSpaces.isEmpty
|
hintText: state.subSpaces.isEmpty
|
||||||
? 'Please enter the name'
|
? 'Please enter the name'
|
||||||
: null,
|
: null,
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.lightGrayColor),
|
||||||
.bodySmall!
|
),
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager
|
|
||||||
.lightGrayColor)),
|
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
if (value.trim().isNotEmpty) {
|
if (value.trim().isNotEmpty) {
|
||||||
context.read<SubSpaceModelBloc>().add(
|
context.read<SubSpaceModelBloc>().add(
|
||||||
@ -158,11 +154,9 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
|
|||||||
textController.clear();
|
textController.clear();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
style: Theme.of(context)
|
style: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.blackColor),
|
||||||
.bodyMedium
|
),
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.blackColor)),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -170,13 +164,13 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
|
|||||||
if (state.errorMessage.isNotEmpty)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16.0),
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
child: Text(state.errorMessage,
|
child: Text(
|
||||||
style: Theme.of(context)
|
state.errorMessage,
|
||||||
.textTheme
|
style: const TextStyle(
|
||||||
.bodySmall
|
|
||||||
?.copyWith(
|
|
||||||
color: ColorsManager.red,
|
color: ColorsManager.red,
|
||||||
)),
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
|
||||||
|
|
||||||
class SpaceHelper {
|
|
||||||
static SpaceModel? findSpaceByUuid(
|
|
||||||
String? uuid, List<CommunityModel> communities) {
|
|
||||||
for (var community in communities) {
|
|
||||||
for (var space in community.spaces) {
|
|
||||||
if (space.uuid == uuid) return space;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SpaceModel? findSpaceByInternalId(
|
|
||||||
String? internalId, List<SpaceModel> spaces) {
|
|
||||||
if (internalId != null) {
|
|
||||||
for (var space in spaces) {
|
|
||||||
if (space.internalId == internalId) return space;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static String generateUniqueSpaceName(
|
|
||||||
String originalName, List<SpaceModel> spaces) {
|
|
||||||
final baseName = originalName.replaceAll(RegExp(r'\(\d+\)$'), '').trim();
|
|
||||||
int maxNumber = 0;
|
|
||||||
|
|
||||||
for (var space in spaces) {
|
|
||||||
final match = RegExp(r'^(.*?)\((\d+)\)$').firstMatch(space.name);
|
|
||||||
if (match != null && match.group(1)?.trim() == baseName) {
|
|
||||||
int existingNumber = int.parse(match.group(2)!);
|
|
||||||
if (existingNumber > maxNumber) {
|
|
||||||
maxNumber = existingNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "$baseName(${maxNumber + 1})";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,144 +1,9 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/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/tag.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/models/tag_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||||
|
|
||||||
class TagHelper {
|
class TagHelper {
|
||||||
static Map<String, dynamic> updateTags<T>({
|
|
||||||
required List<T> updatedTags,
|
|
||||||
required List<dynamic>? subspaces,
|
|
||||||
required String Function(T) getInternalId,
|
|
||||||
required String? Function(T) getLocation,
|
|
||||||
required void Function(T, String) setLocation,
|
|
||||||
required String Function(dynamic) getSubspaceName,
|
|
||||||
required List<T>? Function(dynamic) getSubspaceTags,
|
|
||||||
required void Function(dynamic, List<T>?) setSubspaceTags,
|
|
||||||
required int? Function(T, List<dynamic>) checkTagExistInSubspace,
|
|
||||||
}) {
|
|
||||||
final modifiedTags = List<T>.from(updatedTags);
|
|
||||||
final modifiedSubspaces = List<dynamic>.from(subspaces ?? []);
|
|
||||||
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (var subspace in subspaces) {
|
|
||||||
getSubspaceTags(subspace)?.removeWhere(
|
|
||||||
(tag) => !modifiedTags.any(
|
|
||||||
(updatedTag) => getInternalId(updatedTag) == getInternalId(tag)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var tag in modifiedTags.toList()) {
|
|
||||||
if (modifiedSubspaces.isEmpty) continue;
|
|
||||||
|
|
||||||
final prevIndice = checkTagExistInSubspace(tag, modifiedSubspaces);
|
|
||||||
final tagLocation = getLocation(tag);
|
|
||||||
|
|
||||||
if ((tagLocation == 'Main Space' || tagLocation == null) &&
|
|
||||||
(prevIndice == null ||
|
|
||||||
getSubspaceName(modifiedSubspaces[prevIndice]) == 'Main Space')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tagLocation == 'Main Space' || tagLocation == null) &&
|
|
||||||
prevIndice != null) {
|
|
||||||
getSubspaceTags(modifiedSubspaces[prevIndice])
|
|
||||||
?.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tagLocation != 'Main Space' && tagLocation != null) &&
|
|
||||||
prevIndice == null) {
|
|
||||||
final newIndex = modifiedSubspaces
|
|
||||||
.indexWhere((subspace) => getSubspaceName(subspace) == tagLocation);
|
|
||||||
|
|
||||||
if (newIndex != -1) {
|
|
||||||
if (getSubspaceTags(modifiedSubspaces[newIndex])
|
|
||||||
?.any((t) => getInternalId(t) == getInternalId(tag)) !=
|
|
||||||
true) {
|
|
||||||
setLocation(tag, getSubspaceName(modifiedSubspaces[newIndex]));
|
|
||||||
final subspaceTags =
|
|
||||||
getSubspaceTags(modifiedSubspaces[newIndex]) ?? [];
|
|
||||||
subspaceTags.add(tag);
|
|
||||||
setSubspaceTags(modifiedSubspaces[newIndex], subspaceTags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tagLocation != 'Main Space' && tagLocation != null) &&
|
|
||||||
tagLocation != getSubspaceName(modifiedSubspaces[prevIndice!])) {
|
|
||||||
getSubspaceTags(modifiedSubspaces[prevIndice])
|
|
||||||
?.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
|
|
||||||
final newIndex = modifiedSubspaces
|
|
||||||
.indexWhere((subspace) => getSubspaceName(subspace) == tagLocation);
|
|
||||||
|
|
||||||
if (newIndex != -1) {
|
|
||||||
if (getSubspaceTags(modifiedSubspaces[newIndex])
|
|
||||||
?.any((t) => getInternalId(t) == getInternalId(tag)) !=
|
|
||||||
true) {
|
|
||||||
setLocation(tag, getSubspaceName(modifiedSubspaces[newIndex]));
|
|
||||||
final subspaceTags =
|
|
||||||
getSubspaceTags(modifiedSubspaces[newIndex]) ?? [];
|
|
||||||
subspaceTags.add(tag);
|
|
||||||
setSubspaceTags(modifiedSubspaces[newIndex], subspaceTags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tagLocation != 'Main Space' && tagLocation != null) &&
|
|
||||||
tagLocation == getSubspaceName(modifiedSubspaces[prevIndice!])) {
|
|
||||||
modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((tagLocation == 'Main Space' || tagLocation == null) &&
|
|
||||||
prevIndice != null) {
|
|
||||||
getSubspaceTags(modifiedSubspaces[prevIndice])
|
|
||||||
?.removeWhere((t) => getInternalId(t) == getInternalId(tag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
'updatedTags': modifiedTags,
|
|
||||||
'subspaces': modifiedSubspaces,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<String> getAvailableTags<T>({
|
|
||||||
required List<String> allTags,
|
|
||||||
required List<T> currentTags,
|
|
||||||
required T currentTag,
|
|
||||||
required String? Function(T) getTag, // Allow nullable return type
|
|
||||||
}) {
|
|
||||||
return allTags
|
|
||||||
.where((tagValue) => !currentTags
|
|
||||||
.where((e) => e != currentTag) // Exclude the current row
|
|
||||||
.map((e) => getTag(e) ?? '') // Handle null values gracefully
|
|
||||||
.contains(tagValue))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<String> getAvailableTagModels(
|
|
||||||
List<String> allTags, List<TagModel> currentTags, TagModel currentTag) {
|
|
||||||
List<String> availableTagsForTagModel =
|
|
||||||
TagHelper.getAvailableTags<TagModel>(
|
|
||||||
allTags: allTags,
|
|
||||||
currentTags: currentTags,
|
|
||||||
currentTag: currentTag,
|
|
||||||
getTag: (tag) => tag.tag ?? '',
|
|
||||||
);
|
|
||||||
return availableTagsForTagModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<TagModel> generateInitialTags({
|
static List<TagModel> generateInitialTags({
|
||||||
List<TagModel>? spaceTagModels,
|
List<TagModel>? spaceTagModels,
|
||||||
List<SubspaceTemplateModel>? subspaces,
|
List<SubspaceTemplateModel>? subspaces,
|
||||||
@ -152,52 +17,23 @@ class TagHelper {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (var subspace in subspaces) {
|
for (var subspace in subspaces) {
|
||||||
if (subspace.tags != null) {
|
if (subspace.tags != null) {
|
||||||
|
for (var existingTag in subspace.tags!) {
|
||||||
initialTags.addAll(
|
initialTags.addAll(
|
||||||
subspace.tags!.map(
|
subspace.tags!.map(
|
||||||
(tag) => tag.copyWith(
|
(tag) => tag.copyWith(
|
||||||
location: subspace.subspaceName,
|
location: subspace.subspaceName,
|
||||||
internalId: tag.internalId,
|
internalId: existingTag.internalId,
|
||||||
tag: tag.tag,
|
tag: existingTag.tag),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return initialTags;
|
return initialTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Tag> generateInitialForTags({
|
static Map<ProductModel, int> groupTags(List<TagModel> tags) {
|
||||||
List<Tag>? spaceTags,
|
|
||||||
List<SubspaceModel>? subspaces,
|
|
||||||
}) {
|
|
||||||
final List<Tag> initialTags = [];
|
|
||||||
|
|
||||||
if (spaceTags != null) {
|
|
||||||
initialTags.addAll(spaceTags);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (var subspace in subspaces) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
initialTags.addAll(
|
|
||||||
subspace.tags!.map(
|
|
||||||
(tag) => tag.copyWith(
|
|
||||||
location: subspace.subspaceName,
|
|
||||||
internalId: tag.internalId,
|
|
||||||
tag: tag.tag,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<ProductModel, int> groupTags(List<BaseTag> tags) {
|
|
||||||
final Map<ProductModel, int> groupedTags = {};
|
final Map<ProductModel, int> groupedTags = {};
|
||||||
for (var tag in tags) {
|
for (var tag in tags) {
|
||||||
if (tag.product != null) {
|
if (tag.product != null) {
|
||||||
@ -242,99 +78,4 @@ class TagHelper {
|
|||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<SelectedProduct> createInitialSelectedProductsForTags(
|
|
||||||
List<Tag>? tags, List<SubspaceModel>? subspaces) {
|
|
||||||
final Map<ProductModel, int> productCounts = {};
|
|
||||||
|
|
||||||
if (tags != null) {
|
|
||||||
for (var tag in tags) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subspaces != null) {
|
|
||||||
for (var subspace in subspaces) {
|
|
||||||
if (subspace.tags != null) {
|
|
||||||
for (var tag in subspace.tags!) {
|
|
||||||
if (tag.product != null) {
|
|
||||||
productCounts[tag.product!] =
|
|
||||||
(productCounts[tag.product!] ?? 0) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return productCounts.entries
|
|
||||||
.map((entry) => SelectedProduct(
|
|
||||||
productId: entry.key.uuid,
|
|
||||||
count: entry.value,
|
|
||||||
productName: entry.key.name ?? 'Unnamed',
|
|
||||||
product: entry.key,
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int? checkTagExistInSubspaceModels(
|
|
||||||
TagModel tag, List<dynamic>? subspaces) {
|
|
||||||
if (subspaces == null) return null;
|
|
||||||
|
|
||||||
for (int i = 0; i < subspaces.length; i++) {
|
|
||||||
final subspace = subspaces[i] as SubspaceTemplateModel; // Explicit cast
|
|
||||||
if (subspace.tags == null) continue;
|
|
||||||
for (var t in subspace.tags!) {
|
|
||||||
if (tag.internalId == t.internalId) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, dynamic> updateSubspaceTagModels(
|
|
||||||
List<TagModel> updatedTags, List<SubspaceTemplateModel>? subspaces) {
|
|
||||||
return TagHelper.updateTags<TagModel>(
|
|
||||||
updatedTags: updatedTags,
|
|
||||||
subspaces: subspaces,
|
|
||||||
getInternalId: (tag) => tag.internalId,
|
|
||||||
getLocation: (tag) => tag.location,
|
|
||||||
setLocation: (tag, location) => tag.location = location,
|
|
||||||
getSubspaceName: (subspace) => subspace.subspaceName,
|
|
||||||
getSubspaceTags: (subspace) => subspace.tags,
|
|
||||||
setSubspaceTags: (subspace, tags) => subspace.tags = tags,
|
|
||||||
checkTagExistInSubspace: checkTagExistInSubspaceModels,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int? checkTagExistInSubspace(Tag tag, List<dynamic>? subspaces) {
|
|
||||||
if (subspaces == null) return null;
|
|
||||||
for (int i = 0; i < subspaces.length; i++) {
|
|
||||||
final subspace = subspaces[i];
|
|
||||||
if (subspace.tags == null) continue;
|
|
||||||
for (var t in subspace.tags!) {
|
|
||||||
if (tag.internalId == t.internalId) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, dynamic> processTags(
|
|
||||||
List<Tag> updatedTags, List<SubspaceModel>? subspaces) {
|
|
||||||
return TagHelper.updateTags<Tag>(
|
|
||||||
updatedTags: updatedTags,
|
|
||||||
subspaces: subspaces,
|
|
||||||
getInternalId: (tag) => tag.internalId,
|
|
||||||
getLocation: (tag) => tag.location,
|
|
||||||
setLocation: (tag, location) => tag.location = location,
|
|
||||||
getSubspaceName: (subspace) => subspace.subspaceName,
|
|
||||||
getSubspaceTags: (subspace) => subspace.tags,
|
|
||||||
setSubspaceTags: (subspace, tags) => subspace.tags = tags,
|
|
||||||
checkTagExistInSubspace: checkTagExistInSubspace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -68,11 +68,8 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
on<UpdateSpaceTemplate>((event, emit) {
|
on<UpdateSpaceTemplate>((event, emit) {
|
||||||
_space = event.spaceTemplate;
|
_space = event.spaceTemplate;
|
||||||
final String? errorMessage = _checkDuplicateModelName(
|
emit(CreateSpaceModelLoaded(_space!));
|
||||||
event.allModels ?? [], event.spaceTemplate.modelName);
|
|
||||||
emit(CreateSpaceModelLoaded(_space!, errorMessage: errorMessage));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
on<AddSubspacesToSpaceTemplate>((event, emit) {
|
on<AddSubspacesToSpaceTemplate>((event, emit) {
|
||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
@ -135,8 +132,7 @@ class CreateSpaceModelBloc
|
|||||||
final updatedSpace =
|
final updatedSpace =
|
||||||
currentState.space.copyWith(subspaceModels: updatedSubspaces);
|
currentState.space.copyWith(subspaceModels: updatedSubspaces);
|
||||||
|
|
||||||
emit(CreateSpaceModelLoaded(updatedSpace,
|
emit(CreateSpaceModelLoaded(updatedSpace));
|
||||||
errorMessage: currentState.errorMessage));
|
|
||||||
} else {
|
} else {
|
||||||
emit(CreateSpaceModelError("Space template not initialized"));
|
emit(CreateSpaceModelError("Space template not initialized"));
|
||||||
}
|
}
|
||||||
@ -201,30 +197,26 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
on<ModifySpaceTemplate>((event, emit) async {
|
on<ModifySpaceTemplate>((event, emit) async {
|
||||||
try {
|
try {
|
||||||
if (event.spaceTemplate.uuid != null) {
|
final prevSpaceModel = event.spaceTemplate;
|
||||||
final prevSpaceModel =
|
|
||||||
await _api.getSpaceModel(event.spaceTemplate.uuid ?? '');
|
|
||||||
|
|
||||||
final newSpaceModel = event.updatedSpaceTemplate;
|
final newSpaceModel = event.updatedSpaceTemplate;
|
||||||
String? spaceModelName;
|
String? spaceModelName;
|
||||||
if (prevSpaceModel?.modelName != newSpaceModel.modelName) {
|
if (prevSpaceModel.modelName != newSpaceModel.modelName) {
|
||||||
spaceModelName = newSpaceModel.modelName;
|
spaceModelName = newSpaceModel.modelName;
|
||||||
}
|
}
|
||||||
List<TagModelUpdate> tagUpdates = [];
|
List<TagModelUpdate> tagUpdates = [];
|
||||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||||
final List<SubspaceTemplateModel>? prevSubspaces =
|
final List<SubspaceTemplateModel>? prevSubspaces =
|
||||||
prevSpaceModel?.subspaceModels;
|
prevSpaceModel.subspaceModels;
|
||||||
final List<SubspaceTemplateModel>? newSubspaces =
|
final List<SubspaceTemplateModel>? newSubspaces =
|
||||||
newSpaceModel.subspaceModels;
|
newSpaceModel.subspaceModels;
|
||||||
|
|
||||||
tagUpdates =
|
tagUpdates = processTagUpdates(prevSpaceModel.tags, newSpaceModel.tags);
|
||||||
processTagUpdates(prevSpaceModel?.tags, newSpaceModel.tags);
|
|
||||||
|
|
||||||
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 = newSubspaces
|
final existsInNew = newSubspaces!
|
||||||
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
.any((newTag) => newTag.uuid == prevSubspace.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
action: Action.delete, uuid: prevSubspace.uuid));
|
action: Action.delete, uuid: prevSubspace.uuid));
|
||||||
@ -247,7 +239,6 @@ class CreateSpaceModelBloc
|
|||||||
for (var tag in newSubspace.tags!) {
|
for (var tag in newSubspace.tags!) {
|
||||||
tagUpdates.add(TagModelUpdate(
|
tagUpdates.add(TagModelUpdate(
|
||||||
action: Action.add,
|
action: Action.add,
|
||||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
|
||||||
tag: tag.tag,
|
tag: tag.tag,
|
||||||
productUuid: tag.product?.uuid));
|
productUuid: tag.product?.uuid));
|
||||||
}
|
}
|
||||||
@ -265,9 +256,8 @@ class CreateSpaceModelBloc
|
|||||||
for (var subspace in newSubspaces!) subspace.uuid: subspace
|
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];
|
||||||
|
|
||||||
if (newSubspace != null) {
|
if (newSubspace != null) {
|
||||||
final List<TagModelUpdate> tagSubspaceUpdates =
|
final List<TagModelUpdate> tagSubspaceUpdates =
|
||||||
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
||||||
@ -276,7 +266,7 @@ class CreateSpaceModelBloc
|
|||||||
uuid: newSubspace.uuid,
|
uuid: newSubspace.uuid,
|
||||||
subspaceName: newSubspace.subspaceName,
|
subspaceName: newSubspace.subspaceName,
|
||||||
tags: tagSubspaceUpdates));
|
tags: tagSubspaceUpdates));
|
||||||
}
|
} else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,7 +277,7 @@ class CreateSpaceModelBloc
|
|||||||
subspaceModels: subspaceUpdates);
|
subspaceModels: subspaceUpdates);
|
||||||
|
|
||||||
final res = await _api.updateSpaceModel(
|
final res = await _api.updateSpaceModel(
|
||||||
spaceModelBody, prevSpaceModel?.uuid ?? '');
|
spaceModelBody, prevSpaceModel.uuid ?? '');
|
||||||
|
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
emit(CreateSpaceModelLoaded(newSpaceModel));
|
emit(CreateSpaceModelLoaded(newSpaceModel));
|
||||||
@ -295,7 +285,6 @@ class CreateSpaceModelBloc
|
|||||||
event.onUpdate!(event.updatedSpaceTemplate);
|
event.onUpdate!(event.updatedSpaceTemplate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(CreateSpaceModelError('Error creating space model'));
|
emit(CreateSpaceModelError('Error creating space model'));
|
||||||
}
|
}
|
||||||
@ -309,22 +298,10 @@ class CreateSpaceModelBloc
|
|||||||
final List<TagModelUpdate> tagUpdates = [];
|
final List<TagModelUpdate> tagUpdates = [];
|
||||||
final processedTags = <String?>{};
|
final processedTags = <String?>{};
|
||||||
|
|
||||||
if (prevTags == null && newTags != null) {
|
|
||||||
for (var newTag in newTags) {
|
|
||||||
tagUpdates.add(TagModelUpdate(
|
|
||||||
action: Action.add,
|
|
||||||
tag: newTag.tag,
|
|
||||||
uuid: newTag.uuid,
|
|
||||||
productUuid: newTag.product?.uuid,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return tagUpdates;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTags != null || prevTags != null) {
|
if (newTags != null || prevTags != null) {
|
||||||
// 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 =
|
final existsInNew =
|
||||||
newTags!.any((newTag) => newTag.uuid == prevTag.uuid);
|
newTags!.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
@ -341,16 +318,13 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
// Case 2: Tags added
|
// Case 2: Tags added
|
||||||
if (newTags != null) {
|
if (newTags != null) {
|
||||||
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
|
|
||||||
|
|
||||||
for (var newTag in newTags!) {
|
for (var newTag in newTags!) {
|
||||||
// Tag without UUID
|
// Tag without UUID
|
||||||
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
|
if ((newTag.uuid == null || newTag.uuid!.isEmpty) &&
|
||||||
!processedTags.contains(newTag.tag)) {
|
!processedTags.contains(newTag.tag)) {
|
||||||
tagUpdates.add(TagModelUpdate(
|
tagUpdates.add(TagModelUpdate(
|
||||||
action: Action.add,
|
action: Action.add,
|
||||||
tag: newTag.tag,
|
tag: newTag.tag,
|
||||||
uuid: newTag.uuid == '' ? null : newTag.uuid,
|
|
||||||
productUuid: newTag.product?.uuid));
|
productUuid: newTag.product?.uuid));
|
||||||
processedTags.add(newTag.tag);
|
processedTags.add(newTag.tag);
|
||||||
}
|
}
|
||||||
@ -376,11 +350,4 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
return tagUpdates;
|
return tagUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _checkDuplicateModelName(List<String> allModels, String name) {
|
|
||||||
if (allModels.contains(name)) {
|
|
||||||
return "Duplicate Model name";
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,8 @@ class LoadSpaceTemplate extends CreateSpaceModelEvent {}
|
|||||||
|
|
||||||
class UpdateSpaceTemplate extends CreateSpaceModelEvent {
|
class UpdateSpaceTemplate extends CreateSpaceModelEvent {
|
||||||
final SpaceTemplateModel spaceTemplate;
|
final SpaceTemplateModel spaceTemplate;
|
||||||
List<String>? allModels;
|
|
||||||
|
|
||||||
UpdateSpaceTemplate(this.spaceTemplate,this.allModels);
|
UpdateSpaceTemplate(this.spaceTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
class CreateSpaceTemplate extends CreateSpaceModelEvent {
|
class CreateSpaceTemplate extends CreateSpaceModelEvent {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
class TagBodyModel {
|
class TagBodyModel {
|
||||||
late String? uuid;
|
late String uuid;
|
||||||
late String tag;
|
late String tag;
|
||||||
late final String? productUuid;
|
late final String? productUuid;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/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/models/tag_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_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';
|
||||||
|
@ -20,7 +20,7 @@ class SubspaceTemplateModel {
|
|||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||||
|
|
||||||
return SubspaceTemplateModel(
|
return SubspaceTemplateModel(
|
||||||
uuid: json['uuid'],
|
uuid: json['uuid'] ?? '',
|
||||||
subspaceName: json['subspaceName'] ?? '',
|
subspaceName: json['subspaceName'] ?? '',
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
disabled: json['disabled'] ?? false,
|
disabled: json['disabled'] ?? false,
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class TagModel extends BaseTag {
|
class TagModel {
|
||||||
TagModel({
|
String? uuid;
|
||||||
String? uuid,
|
String? tag;
|
||||||
required String? tag,
|
final ProductModel? product;
|
||||||
ProductModel? product,
|
String internalId;
|
||||||
|
String? location;
|
||||||
|
|
||||||
|
TagModel(
|
||||||
|
{this.uuid,
|
||||||
|
required this.tag,
|
||||||
|
this.product,
|
||||||
String? internalId,
|
String? internalId,
|
||||||
String? location,
|
this.location})
|
||||||
}) : super(
|
: internalId = internalId ?? const Uuid().v4();
|
||||||
uuid: uuid,
|
|
||||||
tag: tag,
|
|
||||||
product: product,
|
|
||||||
internalId: internalId,
|
|
||||||
location: location,
|
|
||||||
);
|
|
||||||
factory TagModel.fromJson(Map<String, dynamic> json) {
|
factory TagModel.fromJson(Map<String, dynamic> json) {
|
||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||||
|
|
||||||
return TagModel(
|
return TagModel(
|
||||||
uuid: json['uuid'] ,
|
uuid: json['uuid'] ?? '',
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
tag: json['tag'] ?? '',
|
tag: json['tag'] ?? '',
|
||||||
product: json['product'] != null
|
product: json['product'] != null
|
||||||
@ -30,11 +30,9 @@ class TagModel extends BaseTag {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
TagModel copyWith(
|
TagModel copyWith(
|
||||||
{String? tag,
|
{String? tag,
|
||||||
ProductModel? product,
|
ProductModel? product,
|
||||||
String? uuid,
|
|
||||||
String? location,
|
String? location,
|
||||||
String? internalId}) {
|
String? internalId}) {
|
||||||
return TagModel(
|
return TagModel(
|
||||||
@ -42,7 +40,6 @@ class TagModel extends BaseTag {
|
|||||||
product: product ?? this.product,
|
product: product ?? this.product,
|
||||||
location: location ?? this.location,
|
location: location ?? this.location,
|
||||||
internalId: internalId ?? this.internalId,
|
internalId: internalId ?? this.internalId,
|
||||||
uuid:uuid?? this.uuid
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +55,7 @@ class TagModel extends BaseTag {
|
|||||||
extension TagModelExtensions on TagModel {
|
extension TagModelExtensions on TagModel {
|
||||||
TagBodyModel toTagBodyModel() {
|
TagBodyModel toTagBodyModel() {
|
||||||
return TagBodyModel()
|
return TagBodyModel()
|
||||||
..uuid = uuid
|
..uuid = uuid ?? ''
|
||||||
..tag = tag ?? ''
|
..tag = tag ?? ''
|
||||||
..productUuid = product?.uuid;
|
..productUuid = product?.uuid;
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,8 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
|||||||
|
|
||||||
class SpaceModelPage extends StatelessWidget {
|
class SpaceModelPage extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final Function(List<SpaceTemplateModel>)? onSpaceModelsUpdated;
|
|
||||||
|
|
||||||
const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated})
|
const SpaceModelPage({Key? key, this.products}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -27,10 +25,6 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
final allTagValues = _getAllTagValues(spaceModels);
|
final allTagValues = _getAllTagValues(spaceModels);
|
||||||
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
||||||
|
|
||||||
if (onSpaceModelsUpdated != null) {
|
|
||||||
onSpaceModelsUpdated!(spaceModels);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
body: Padding(
|
body: Padding(
|
||||||
@ -69,8 +63,7 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
// Render existing space model
|
// Render existing space model
|
||||||
final model = spaceModels[index];
|
final model = spaceModels[index];
|
||||||
final otherModel =
|
final otherModel = List<String>.from(allSpaceModelNames);
|
||||||
List<String>.from(allSpaceModelNames);
|
|
||||||
otherModel.remove(model.modelName);
|
otherModel.remove(model.modelName);
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -83,7 +76,6 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
spaceModel: model,
|
spaceModel: model,
|
||||||
otherSpaceModels: otherModel,
|
otherSpaceModels: otherModel,
|
||||||
pageContext: context,
|
pageContext: context,
|
||||||
allSpaceModels: spaceModels,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -103,10 +95,7 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Error: ${state.message}',
|
'Error: ${state.message}',
|
||||||
style: Theme.of(context)
|
style: const TextStyle(color: ColorsManager.warningRed),
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.warningRed),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -118,14 +107,14 @@ class SpaceModelPage extends StatelessWidget {
|
|||||||
double _calculateChildAspectRatio(BuildContext context) {
|
double _calculateChildAspectRatio(BuildContext context) {
|
||||||
double screenWidth = MediaQuery.of(context).size.width;
|
double screenWidth = MediaQuery.of(context).size.width;
|
||||||
if (screenWidth > 1600) {
|
if (screenWidth > 1600) {
|
||||||
return 1.5; // Decrease to make cards taller
|
return 2;
|
||||||
}
|
}
|
||||||
if (screenWidth > 1200) {
|
if (screenWidth > 1200) {
|
||||||
return 2.0;
|
return 3;
|
||||||
} else if (screenWidth > 800) {
|
} else if (screenWidth > 800) {
|
||||||
return 2.5;
|
return 3.5;
|
||||||
} else {
|
} else {
|
||||||
return 3.0;
|
return 4.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class ButtonContentWidget extends StatelessWidget {
|
class ButtonContentWidget extends StatelessWidget {
|
||||||
final IconData? icon;
|
final IconData icon;
|
||||||
final String label;
|
final String label;
|
||||||
final String? svgAssets;
|
|
||||||
|
|
||||||
const ButtonContentWidget(
|
const ButtonContentWidget({
|
||||||
{Key? key, this.icon, required this.label, this.svgAssets})
|
Key? key,
|
||||||
: super(key: key);
|
required this.icon,
|
||||||
|
required this.label,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -30,20 +30,10 @@ class ButtonContentWidget extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (icon != null)
|
|
||||||
Icon(
|
Icon(
|
||||||
icon,
|
icon,
|
||||||
color: ColorsManager.spaceColor,
|
color: ColorsManager.spaceColor,
|
||||||
),
|
),
|
||||||
if (svgAssets != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 6.0),
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
svgAssets!,
|
|
||||||
width: screenWidth * 0.015, // Adjust icon size
|
|
||||||
height: screenWidth * 0.015,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -22,7 +22,6 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
final SpaceTemplateModel? spaceModel;
|
final SpaceTemplateModel? spaceModel;
|
||||||
final BuildContext? pageContext;
|
final BuildContext? pageContext;
|
||||||
final List<String>? otherSpaceModels;
|
final List<String>? otherSpaceModels;
|
||||||
final List<SpaceTemplateModel>? allSpaceModels;
|
|
||||||
|
|
||||||
const CreateSpaceModelDialog(
|
const CreateSpaceModelDialog(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
@ -30,8 +29,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
this.allTags,
|
this.allTags,
|
||||||
this.spaceModel,
|
this.spaceModel,
|
||||||
this.pageContext,
|
this.pageContext,
|
||||||
this.otherSpaceModels,
|
this.otherSpaceModels})
|
||||||
this.allSpaceModels})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -52,14 +50,12 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
create: (_) {
|
create: (_) {
|
||||||
final bloc = CreateSpaceModelBloc(_spaceModelApi);
|
final bloc = CreateSpaceModelBloc(_spaceModelApi);
|
||||||
if (spaceModel != null) {
|
if (spaceModel != null) {
|
||||||
bloc.add(UpdateSpaceTemplate(spaceModel!, otherSpaceModels));
|
bloc.add(UpdateSpaceTemplate(spaceModel!));
|
||||||
} else {
|
} else {
|
||||||
bloc.add(UpdateSpaceTemplate(
|
bloc.add(UpdateSpaceTemplate(SpaceTemplateModel(
|
||||||
SpaceTemplateModel(
|
|
||||||
modelName: '',
|
modelName: '',
|
||||||
subspaceModels: const [],
|
subspaceModels: const [],
|
||||||
),
|
)));
|
||||||
otherSpaceModels));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spaceNameController.addListener(() {
|
spaceNameController.addListener(() {
|
||||||
@ -102,19 +98,14 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
name: value,
|
name: value,
|
||||||
allModels: otherSpaceModels ?? []));
|
allModels: otherSpaceModels ?? []));
|
||||||
},
|
},
|
||||||
style: Theme.of(context)
|
style: const TextStyle(color: ColorsManager.blackColor),
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: ColorsManager.blackColor),
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: ColorsManager.textFieldGreyColor,
|
fillColor: ColorsManager.textFieldGreyColor,
|
||||||
hintText: 'Please enter the name',
|
hintText: 'Please enter the name',
|
||||||
errorText: state.errorMessage,
|
errorText: state.errorMessage,
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.lightGrayColor),
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: ColorsManager.lightGrayColor),
|
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
borderSide: BorderSide.none,
|
borderSide: BorderSide.none,
|
||||||
@ -128,17 +119,12 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
SubspaceModelCreate(
|
SubspaceModelCreate(
|
||||||
|
context,
|
||||||
subspaces: state.space.subspaceModels ?? [],
|
subspaces: state.space.subspaceModels ?? [],
|
||||||
tags: state.space.tags ?? [],
|
onSpaceModelUpdate: (updatedSubspaces) {
|
||||||
onSpaceModelUpdate: (updatedSubspaces,updatedTags) {
|
|
||||||
context
|
context
|
||||||
.read<CreateSpaceModelBloc>()
|
.read<CreateSpaceModelBloc>()
|
||||||
.add(AddSubspacesToSpaceTemplate(updatedSubspaces));
|
.add(AddSubspacesToSpaceTemplate(updatedSubspaces));
|
||||||
if(updatedTags!=null){
|
|
||||||
context
|
|
||||||
.read<CreateSpaceModelBloc>()
|
|
||||||
.add(AddTagsToSpaceTemplate(updatedTags));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
@ -152,7 +138,6 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
spaceNameController: spaceNameController,
|
spaceNameController: spaceNameController,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
allSpaceModels: allSpaceModels,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -162,19 +147,15 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: CancelButton(
|
child: CancelButton(
|
||||||
label: 'Cancel',
|
label: 'Cancel',
|
||||||
onPressed: () {
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: ((state.errorMessage != null &&
|
onPressed: state.errorMessage == null ||
|
||||||
state.errorMessage != '') ||
|
isNameValid
|
||||||
!isNameValid)
|
? () {
|
||||||
? null
|
|
||||||
: () {
|
|
||||||
final updatedSpaceTemplate =
|
final updatedSpaceTemplate =
|
||||||
updatedSpaceModel.copyWith(
|
updatedSpaceModel.copyWith(
|
||||||
modelName:
|
modelName:
|
||||||
@ -247,14 +228,13 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
: null,
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
foregroundColor: ((state.errorMessage != null &&
|
foregroundColor: isNameValid
|
||||||
state.errorMessage != '') ||
|
? ColorsManager.whiteColors
|
||||||
!isNameValid)
|
: ColorsManager.whiteColorsWithOpacity,
|
||||||
? ColorsManager.whiteColorsWithOpacity
|
|
||||||
: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -266,10 +246,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
|||||||
} else if (state is CreateSpaceModelError) {
|
} else if (state is CreateSpaceModelError) {
|
||||||
return Text(
|
return Text(
|
||||||
'Error: ${state.message}',
|
'Error: ${state.message}',
|
||||||
style: Theme.of(context)
|
style: const TextStyle(color: ColorsManager.warningRed),
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.warningRed),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class DynamicRoomWidget extends StatelessWidget {
|
|||||||
final TextPainter textPainter = TextPainter(
|
final TextPainter textPainter = TextPainter(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: subspace.subspaceName,
|
text: subspace.subspaceName,
|
||||||
style: Theme.of(context).textTheme.bodyMedium
|
style: const TextStyle(fontSize: 16),
|
||||||
),
|
),
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
)..layout();
|
)..layout();
|
||||||
|
@ -31,9 +31,6 @@ class SpaceModelCardWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return LayoutBuilder(
|
|
||||||
builder: (context, constraints) {
|
|
||||||
bool showOnlyName = constraints.maxWidth < 250;
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -60,7 +57,6 @@ class SpaceModelCardWidget extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
if (!showOnlyName) ...[
|
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -84,8 +80,7 @@ class SpaceModelCardWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (productTagCount.isNotEmpty &&
|
if (productTagCount.isNotEmpty && model.subspaceModels != null)
|
||||||
model.subspaceModels != null)
|
|
||||||
Container(
|
Container(
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
color: ColorsManager.softGray,
|
color: ColorsManager.softGray,
|
||||||
@ -110,11 +105,8 @@ class SpaceModelCardWidget extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,47 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/common/edit_chip.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/models/tag_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/button_content_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.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';
|
||||||
|
|
||||||
class SubspaceModelCreate extends StatefulWidget {
|
class SubspaceModelCreate extends StatelessWidget {
|
||||||
final List<SubspaceTemplateModel> subspaces;
|
final List<SubspaceTemplateModel> subspaces;
|
||||||
final void Function(
|
final void Function(List<SubspaceTemplateModel> newSubspaces)?
|
||||||
List<SubspaceTemplateModel> newSubspaces, List<TagModel>? tags)?
|
|
||||||
onSpaceModelUpdate;
|
onSpaceModelUpdate;
|
||||||
final List<TagModel> tags;
|
|
||||||
|
|
||||||
const SubspaceModelCreate({
|
const SubspaceModelCreate(BuildContext context,
|
||||||
Key? key,
|
{Key? key, required this.subspaces, this.onSpaceModelUpdate})
|
||||||
required this.subspaces,
|
: super(key: key);
|
||||||
this.onSpaceModelUpdate,
|
|
||||||
required this.tags,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_SubspaceModelCreateState createState() => _SubspaceModelCreateState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
|
||||||
late List<SubspaceTemplateModel> _subspaces;
|
|
||||||
String? errorSubspaceId;
|
|
||||||
late List<TagModel> _tags;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_subspaces = List.from(widget.subspaces);
|
|
||||||
_tags = List.from(widget.tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
return Container(
|
return Container(
|
||||||
child: _subspaces.isEmpty
|
child: subspaces.isEmpty
|
||||||
? TextButton(
|
? TextButton(
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
overlayColor: ColorsManager.transparentColor,
|
overlayColor: ColorsManager.transparentColor,
|
||||||
@ -63,37 +39,46 @@ class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
|||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: ColorsManager.textFieldGreyColor,
|
color: ColorsManager.textFieldGreyColor,
|
||||||
width: 3.0,
|
width: 3.0, // Border width
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
spacing: 8.0,
|
spacing: 8.0,
|
||||||
runSpacing: 8.0,
|
runSpacing: 8.0,
|
||||||
children: [
|
children: [
|
||||||
..._subspaces.map((subspace) {
|
...subspaces.map((subspace) => Container(
|
||||||
return Column(
|
padding: const EdgeInsets.symmetric(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
horizontal: 8.0, vertical: 4.0),
|
||||||
children: [
|
decoration: BoxDecoration(
|
||||||
SubspaceNameDisplayWidget(
|
color: ColorsManager.whiteColors,
|
||||||
text: subspace.subspaceName,
|
borderRadius: BorderRadius.circular(10),
|
||||||
validateName: (updatedName) {
|
border: Border.all(
|
||||||
return !_subspaces.any((s) =>
|
color: ColorsManager.transparentColor),
|
||||||
s != subspace &&
|
|
||||||
s.subspaceName == updatedName);
|
|
||||||
},
|
|
||||||
onNameChanged: (updatedName) {
|
|
||||||
setState(() {
|
|
||||||
subspace.subspaceName = updatedName;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
child: Text(
|
||||||
);
|
subspace.subspaceName,
|
||||||
}),
|
style: Theme.of(context)
|
||||||
EditChip(
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _openDialog(context, 'Edit Sub-space');
|
await _openDialog(context, 'Edit Sub-space');
|
||||||
},
|
},
|
||||||
|
child: Chip(
|
||||||
|
label: const Text(
|
||||||
|
'Edit',
|
||||||
|
style: TextStyle(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side:
|
||||||
|
const BorderSide(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -110,28 +95,9 @@ class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
|||||||
return CreateSubSpaceModelDialog(
|
return CreateSubSpaceModelDialog(
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
dialogTitle: dialogTitle,
|
dialogTitle: dialogTitle,
|
||||||
existingSubSpaces: _subspaces,
|
existingSubSpaces: subspaces,
|
||||||
onUpdate: (subspaceModels) {
|
onUpdate: (subspaceModels) {
|
||||||
final updatedIds = subspaceModels.map((s) => s.internalId).toSet();
|
onSpaceModelUpdate!(subspaceModels);
|
||||||
final deletedSubspaces = _subspaces
|
|
||||||
.where((s) => !updatedIds.contains(s.internalId))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
final List<TagModel> tagsToAppendToSpace = [];
|
|
||||||
|
|
||||||
for (var s in deletedSubspaces) {
|
|
||||||
if (s.tags != null) {
|
|
||||||
tagsToAppendToSpace.addAll(s.tags!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_subspaces = subspaceModels;
|
|
||||||
_tags.addAll(tagsToAppendToSpace);
|
|
||||||
});
|
|
||||||
if (widget.onSpaceModelUpdate != null) {
|
|
||||||
widget.onSpaceModelUpdate!(subspaceModels, _tags);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SubspaceNameDisplayWidget extends StatefulWidget {
|
|
||||||
final String text;
|
|
||||||
final TextStyle? textStyle;
|
|
||||||
final Color backgroundColor;
|
|
||||||
final Color borderColor;
|
|
||||||
final EdgeInsetsGeometry padding;
|
|
||||||
final BorderRadiusGeometry borderRadius;
|
|
||||||
final void Function(String updatedName) onNameChanged;
|
|
||||||
final bool Function(String updatedName) validateName;
|
|
||||||
|
|
||||||
const SubspaceNameDisplayWidget({
|
|
||||||
Key? key,
|
|
||||||
required this.text,
|
|
||||||
this.textStyle,
|
|
||||||
this.backgroundColor = ColorsManager.whiteColors,
|
|
||||||
this.borderColor = ColorsManager.transparentColor,
|
|
||||||
this.padding = const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
|
||||||
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
|
|
||||||
required this.onNameChanged,
|
|
||||||
required this.validateName,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_SubspaceNameDisplayWidgetState createState() =>
|
|
||||||
_SubspaceNameDisplayWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SubspaceNameDisplayWidgetState extends State<SubspaceNameDisplayWidget> {
|
|
||||||
bool isEditing = false;
|
|
||||||
late TextEditingController _controller;
|
|
||||||
late FocusNode _focusNode;
|
|
||||||
late String previousName;
|
|
||||||
String? errorText;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller = TextEditingController(text: widget.text);
|
|
||||||
_focusNode = FocusNode();
|
|
||||||
previousName = widget.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
_focusNode.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _handleValidationAndSave() {
|
|
||||||
final updatedName = _controller.text;
|
|
||||||
|
|
||||||
if (updatedName.isEmpty) {
|
|
||||||
setState(() {
|
|
||||||
errorText = 'Subspace name cannot be empty.';
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (widget.validateName(updatedName)) {
|
|
||||||
setState(() {
|
|
||||||
errorText = null;
|
|
||||||
isEditing = false;
|
|
||||||
previousName = updatedName;
|
|
||||||
widget.onNameChanged(updatedName);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setState(() {
|
|
||||||
errorText = 'Subspace name already exists.';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
setState(() {
|
|
||||||
isEditing = true;
|
|
||||||
_focusNode.requestFocus();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: widget.padding,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: widget.backgroundColor,
|
|
||||||
borderRadius: widget.borderRadius,
|
|
||||||
border: Border.all(color: widget.borderColor),
|
|
||||||
),
|
|
||||||
child: isEditing
|
|
||||||
? TextField(
|
|
||||||
controller: _controller,
|
|
||||||
focusNode: _focusNode,
|
|
||||||
style: widget.textStyle ??
|
|
||||||
Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
autofocus: true,
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
contentPadding: EdgeInsets.symmetric(horizontal: 8.0),
|
|
||||||
),
|
|
||||||
onSubmitted: (value) {
|
|
||||||
_handleValidationAndSave();
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
widget.text,
|
|
||||||
style: widget.textStyle ??
|
|
||||||
Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (errorText != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 4.0),
|
|
||||||
child: Text(
|
|
||||||
errorText!,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.warningRed),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:syncrow_web/common/edit_chip.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/assign_tag_models/views/assign_tag_models_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||||
@ -19,7 +18,6 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
final TextEditingController spaceNameController;
|
final TextEditingController spaceNameController;
|
||||||
final BuildContext? pageContext;
|
final BuildContext? pageContext;
|
||||||
final List<String>? otherSpaceModels;
|
final List<String>? otherSpaceModels;
|
||||||
final List<SpaceTemplateModel>? allSpaceModels;
|
|
||||||
|
|
||||||
const TagChipDisplay(BuildContext context,
|
const TagChipDisplay(BuildContext context,
|
||||||
{Key? key,
|
{Key? key,
|
||||||
@ -30,8 +28,7 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
required this.allTags,
|
required this.allTags,
|
||||||
required this.spaceNameController,
|
required this.spaceNameController,
|
||||||
this.pageContext,
|
this.pageContext,
|
||||||
this.otherSpaceModels,
|
this.otherSpaceModels})
|
||||||
this.allSpaceModels})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -73,12 +70,9 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
label: Text(
|
label: Text(
|
||||||
'x${entry.value}', // Show count
|
'x${entry.value}', // Show count
|
||||||
style: Theme.of(context)
|
style: const TextStyle(
|
||||||
.textTheme
|
color: ColorsManager.spaceColor,
|
||||||
.bodySmall!
|
),
|
||||||
.copyWith(
|
|
||||||
color:
|
|
||||||
ColorsManager.spaceColor),
|
|
||||||
),
|
),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -89,21 +83,22 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
EditChip(onTap: () async {
|
GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
// Use the Navigator's context for showDialog
|
// Use the Navigator's context for showDialog
|
||||||
Navigator.of(context).pop();
|
final navigatorContext =
|
||||||
|
Navigator.of(context).overlay?.context;
|
||||||
|
|
||||||
|
if (navigatorContext != null) {
|
||||||
await showDialog<bool>(
|
await showDialog<bool>(
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
context: context,
|
context: navigatorContext,
|
||||||
builder: (context) => AssignTagModelsDialog(
|
builder: (context) => AssignTagModelsDialog(
|
||||||
products: products,
|
products: products,
|
||||||
allSpaceModels: allSpaceModels,
|
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceModel: spaceModel,
|
spaceModel: spaceModel,
|
||||||
otherSpaceModels: otherSpaceModels,
|
|
||||||
initialTags: TagHelper.generateInitialTags(
|
initialTags: TagHelper.generateInitialTags(
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
spaceTagModels: spaceModel?.tags ?? []),
|
spaceTagModels: spaceModel?.tags ?? []),
|
||||||
@ -113,7 +108,20 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
spaceModel?.tags ?? [], subspaces),
|
spaceModel?.tags ?? [], subspaces),
|
||||||
spaceName: spaceModel?.modelName ?? '',
|
spaceName: spaceModel?.modelName ?? '',
|
||||||
));
|
));
|
||||||
})
|
}
|
||||||
|
},
|
||||||
|
child: Chip(
|
||||||
|
label: const Text(
|
||||||
|
'Edit',
|
||||||
|
style: TextStyle(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -133,7 +141,6 @@ class TagChipDisplay extends StatelessWidget {
|
|||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
isCreate: true,
|
isCreate: true,
|
||||||
spaceModel: spaceModel,
|
spaceModel: spaceModel,
|
||||||
otherSpaceModels: otherSpaceModels,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.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/helper/tag_helper.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/models/tag_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||||
@ -27,7 +26,6 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
final List<String>? otherSpaceModels;
|
final List<String>? otherSpaceModels;
|
||||||
final BuildContext? pageContext;
|
final BuildContext? pageContext;
|
||||||
final SpaceTemplateModel? spaceModel;
|
final SpaceTemplateModel? spaceModel;
|
||||||
final List<SpaceTemplateModel>? allSpaceModels;
|
|
||||||
|
|
||||||
const AddDeviceTypeModelWidget(
|
const AddDeviceTypeModelWidget(
|
||||||
{super.key,
|
{super.key,
|
||||||
@ -40,8 +38,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
required this.isCreate,
|
required this.isCreate,
|
||||||
this.pageContext,
|
this.pageContext,
|
||||||
this.otherSpaceModels,
|
this.otherSpaceModels,
|
||||||
this.spaceModel,
|
this.spaceModel});
|
||||||
this.allSpaceModels});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -109,7 +106,6 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext dialogContext) {
|
builder: (BuildContext dialogContext) {
|
||||||
return CreateSpaceModelDialog(
|
return CreateSpaceModelDialog(
|
||||||
allSpaceModels: allSpaceModels,
|
|
||||||
products: products,
|
products: products,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
@ -124,7 +120,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final initialTags = TagHelper.generateInitialTags(
|
final initialTags = generateInitialTags(
|
||||||
spaceTagModels: spaceTagModels,
|
spaceTagModels: spaceTagModels,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
);
|
);
|
||||||
@ -166,8 +162,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
: () async {
|
: () async {
|
||||||
if (state is AddDeviceModelLoaded &&
|
if (state is AddDeviceModelLoaded &&
|
||||||
state.selectedProducts.isNotEmpty) {
|
state.selectedProducts.isNotEmpty) {
|
||||||
final initialTags =
|
final initialTags = generateInitialTags(
|
||||||
TagHelper.generateInitialTags(
|
|
||||||
spaceTagModels: spaceTagModels,
|
spaceTagModels: spaceTagModels,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
);
|
);
|
||||||
@ -180,12 +175,11 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AssignTagModelsDialog(
|
builder: (context) => AssignTagModelsDialog(
|
||||||
products: products,
|
products: products,
|
||||||
allSpaceModels: allSpaceModels,
|
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
addedProducts: state.selectedProducts,
|
addedProducts: state.selectedProducts,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
initialTags: initialTags,
|
initialTags: state.initialTag,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
title: dialogTitle,
|
title: dialogTitle,
|
||||||
spaceModel: spaceModel,
|
spaceModel: spaceModel,
|
||||||
@ -206,4 +200,29 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<TagModel> generateInitialTags({
|
||||||
|
List<TagModel>? spaceTagModels,
|
||||||
|
List<SubspaceTemplateModel>? subspaces,
|
||||||
|
}) {
|
||||||
|
final List<TagModel> initialTags = [];
|
||||||
|
|
||||||
|
if (spaceTagModels != null) {
|
||||||
|
initialTags.addAll(spaceTagModels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subspaces != null) {
|
||||||
|
for (var subspace in subspaces) {
|
||||||
|
if (subspace.tags != null) {
|
||||||
|
initialTags.addAll(
|
||||||
|
subspace.tags!.map(
|
||||||
|
(tag) => tag.copyWith(location: subspace.subspaceName),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,4 +12,33 @@ class HomeApi {
|
|||||||
});
|
});
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future fetchTerms() async {
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: ApiEndpoints.terms,
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future fetchPolicy() async {
|
||||||
|
final response = await HTTPService().get(
|
||||||
|
path: ApiEndpoints.policy,
|
||||||
|
showServerMessage: true,
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future confirmUserAgreements(uuid) async {
|
||||||
|
final response = await HTTPService().patch(
|
||||||
|
path: ApiEndpoints.userAgreements.replaceAll('{userUuid}', uuid!),
|
||||||
|
expectedResponseModel: (json) {
|
||||||
|
return json['data'];
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subs
|
|||||||
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/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/tag_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
|
||||||
import 'package:syncrow_web/services/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 'package:syncrow_web/utils/constants/temp_const.dart';
|
import 'package:syncrow_web/utils/constants/temp_const.dart';
|
||||||
@ -156,7 +154,7 @@ class CommunitySpaceManagementApi {
|
|||||||
.replaceAll('{spaceId}', spaceId)
|
.replaceAll('{spaceId}', spaceId)
|
||||||
.replaceAll('{projectId}', TempConst.projectId),
|
.replaceAll('{projectId}', TempConst.projectId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return SpaceModel.fromJson(json['data']);
|
return SpaceModel.fromJson(json);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
@ -212,7 +210,7 @@ class CommunitySpaceManagementApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateSpace({
|
Future<SpaceModel?> updateSpace({
|
||||||
required String communityId,
|
required String communityId,
|
||||||
required spaceId,
|
required spaceId,
|
||||||
required String name,
|
required String name,
|
||||||
@ -221,8 +219,6 @@ class CommunitySpaceManagementApi {
|
|||||||
String? direction,
|
String? direction,
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
List<TagModelUpdate>? tags,
|
|
||||||
List<UpdateSubspaceTemplateModel>? subspaces,
|
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
final body = {
|
final body = {
|
||||||
@ -232,8 +228,6 @@ class CommunitySpaceManagementApi {
|
|||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
'direction': direction,
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
'subspace': subspaces,
|
|
||||||
'tags': tags,
|
|
||||||
};
|
};
|
||||||
if (parentId != null) {
|
if (parentId != null) {
|
||||||
body['parentUuid'] = parentId;
|
body['parentUuid'] = parentId;
|
||||||
@ -246,13 +240,13 @@ class CommunitySpaceManagementApi {
|
|||||||
.replaceAll('{projectId}', TempConst.projectId),
|
.replaceAll('{projectId}', TempConst.projectId),
|
||||||
body: body,
|
body: body,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
return json['success'] ?? false;
|
return SpaceModel.fromJson(json['data']);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error updating space: $e');
|
debugPrint('Error creating space: $e');
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,5 +70,5 @@ abstract class ColorsManager {
|
|||||||
static const Color invitedOrangeText = Color(0xFFFFBF00);
|
static const Color invitedOrangeText = Color(0xFFFFBF00);
|
||||||
static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
|
static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
|
||||||
//background: #F8F8F8;
|
//background: #F8F8F8;
|
||||||
static const Color vividBlue = Color(0xFF023DFE);
|
|
||||||
}
|
}
|
||||||
|
@ -117,6 +117,7 @@ abstract class ApiEndpoints {
|
|||||||
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
||||||
static const String changeUserStatus =
|
static const String changeUserStatus =
|
||||||
'/invite-user/{invitedUserUuid}/disable';
|
'/invite-user/{invitedUserUuid}/disable';
|
||||||
|
static const String terms = '/terms';
|
||||||
// static const String updateAutomation = '/automation/{automationId}';
|
static const String policy = '/policy';
|
||||||
|
static const String userAgreements = '/user/agreements/web/{userUuid}';
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,6 @@ class Assets {
|
|||||||
|
|
||||||
static const String delete = 'assets/icons/delete.svg';
|
static const String delete = 'assets/icons/delete.svg';
|
||||||
static const String edit = 'assets/icons/edit.svg';
|
static const String edit = 'assets/icons/edit.svg';
|
||||||
static const String editSpace = 'assets/icons/edit_space.svg';
|
|
||||||
//assets/icons/routine/tab_to_run.svg
|
//assets/icons/routine/tab_to_run.svg
|
||||||
static const String tabToRun = 'assets/icons/routine/tab_to_run.svg';
|
static const String tabToRun = 'assets/icons/routine/tab_to_run.svg';
|
||||||
|
|
||||||
@ -399,7 +398,5 @@ class Assets {
|
|||||||
static const String ZtoAIcon = 'assets/icons/ztoa_icon.png';
|
static const String ZtoAIcon = 'assets/icons/ztoa_icon.png';
|
||||||
static const String AtoZIcon = 'assets/icons/atoz_icon.png';
|
static const String AtoZIcon = 'assets/icons/atoz_icon.png';
|
||||||
static const String link = 'assets/icons/link.svg';
|
static const String link = 'assets/icons/link.svg';
|
||||||
static const String duplicate = 'assets/icons/duplicate.svg';
|
|
||||||
static const String spaceDelete = 'assets/icons/space_delete.svg';
|
|
||||||
}
|
}
|
||||||
//user_management.svg
|
//user_management.svg
|
||||||
|
@ -7,9 +7,13 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_linux
|
flutter_secure_storage_linux
|
||||||
|
url_launcher_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
@ -8,9 +8,11 @@ import Foundation
|
|||||||
import flutter_secure_storage_macos
|
import flutter_secure_storage_macos
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import url_launcher_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,8 @@ dependencies:
|
|||||||
time_picker_spinner: ^1.0.0
|
time_picker_spinner: ^1.0.0
|
||||||
intl_phone_field: ^3.2.0
|
intl_phone_field: ^3.2.0
|
||||||
number_pagination: ^1.1.6
|
number_pagination: ^1.1.6
|
||||||
|
url_launcher: ^6.2.5
|
||||||
|
flutter_html: ^3.0.0-beta.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
@ -7,8 +7,11 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
flutter_secure_storage_windows
|
flutter_secure_storage_windows
|
||||||
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
Reference in New Issue
Block a user