mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-11 15:47:44 +00:00
Compare commits
1 Commits
bugfix/fix
...
side_tree
Author | SHA1 | Date | |
---|---|---|---|
132cafcaa2 |
@ -31,8 +31,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
|
||||
////////////////////////////// forget password //////////////////////////////////
|
||||
final TextEditingController forgetEmailController = TextEditingController();
|
||||
final TextEditingController forgetPasswordController =
|
||||
TextEditingController();
|
||||
final TextEditingController forgetPasswordController = TextEditingController();
|
||||
final TextEditingController forgetOtp = TextEditingController();
|
||||
final forgetFormKey = GlobalKey<FormState>();
|
||||
final forgetEmailKey = GlobalKey<FormState>();
|
||||
@ -49,8 +48,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
return;
|
||||
}
|
||||
_remainingTime = 1;
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
try {
|
||||
forgetEmailValidate = '';
|
||||
_remainingTime = (await AuthenticationAPI.sendOtp(
|
||||
@ -87,8 +85,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
_timer?.cancel();
|
||||
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||
} else {
|
||||
add(UpdateTimerEvent(
|
||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -98,8 +95,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
}
|
||||
|
||||
Future<void> changePassword(
|
||||
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
var response = await AuthenticationAPI.verifyOtp(
|
||||
@ -115,8 +111,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage =
|
||||
errorData['error']['message'] ?? 'something went wrong';
|
||||
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
@ -130,9 +125,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
||||
emit(TimerState(
|
||||
isButtonEnabled: event.isButtonEnabled,
|
||||
remainingTime: event.remainingTime));
|
||||
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
|
||||
}
|
||||
|
||||
///////////////////////////////////// login /////////////////////////////////////
|
||||
@ -168,23 +161,15 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
password: event.password,
|
||||
),
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['error']['message'];
|
||||
if (errorMessage == "Access denied for web platform") {
|
||||
validate = errorMessage;
|
||||
} else {
|
||||
validate = 'Invalid Credentials!';
|
||||
}
|
||||
} catch (failure) {
|
||||
validate = 'Invalid Credentials!';
|
||||
emit(LoginInitial());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (token.accessTokenIsNotEmpty) {
|
||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||
await storage.write(
|
||||
key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||
const FlutterSecureStorage().write(
|
||||
key: UserModel.userUuidKey,
|
||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||
@ -342,14 +327,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
static Future<String> getTokenAndValidate() async {
|
||||
try {
|
||||
const storage = FlutterSecureStorage();
|
||||
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
|
||||
StringsManager.firstLaunch) ??
|
||||
true;
|
||||
final firstLaunch =
|
||||
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
|
||||
if (firstLaunch) {
|
||||
storage.deleteAll();
|
||||
}
|
||||
await SharedPreferencesHelper.saveBoolToSP(
|
||||
StringsManager.firstLaunch, false);
|
||||
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
|
||||
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
||||
if (value.isEmpty) {
|
||||
return 'Token not found';
|
||||
@ -402,9 +385,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
final String formattedTime = [
|
||||
if (days > 0) '${days}d', // Append 'd' for days
|
||||
if (days > 0 || hours > 0)
|
||||
hours
|
||||
.toString()
|
||||
.padLeft(2, '0'), // Show hours if there are days or hours
|
||||
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
|
||||
minutes.toString().padLeft(2, '0'),
|
||||
seconds.toString().padLeft(2, '0'),
|
||||
].join(':');
|
||||
|
@ -21,9 +21,7 @@ class LoginWithEmailModel {
|
||||
return {
|
||||
'email': email,
|
||||
'password': password,
|
||||
"platform": "web"
|
||||
// 'regionUuid': regionUuid,
|
||||
};
|
||||
}
|
||||
}
|
||||
//tst@tst.com
|
@ -69,7 +69,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
},
|
||||
)),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
flex: 4,
|
||||
child: state is DeviceManagementLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
|
@ -62,7 +62,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
emit(LoadingHome());
|
||||
terms = await HomeApi().fetchTerms();
|
||||
add(FetchPolicyEvent());
|
||||
emit(PolicyAgreement());
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
@ -72,7 +71,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
||||
try {
|
||||
emit(LoadingHome());
|
||||
policy = await HomeApi().fetchPolicy();
|
||||
emit(PolicyAgreement());
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
||||
final scrollPosition = _scrollController.position;
|
||||
if (scrollPosition.maxScrollExtent <= 0) {
|
||||
setState(() {
|
||||
_isAtEnd = true;
|
||||
_isAtEnd = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -63,11 +63,9 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
||||
}
|
||||
|
||||
String get _dialogTitle =>
|
||||
_currentPage == 1 ? 'User Agreement' : 'Privacy Policy';
|
||||
_currentPage == 2 ? 'User Agreement' : 'Privacy Policy';
|
||||
|
||||
String get _dialogContent => _currentPage == 1 ? widget.terms : widget.policy;
|
||||
final String staticText =
|
||||
'<h5 style="color: #FF5722;">If you cancel you will be logged out.</h5>';
|
||||
String get _dialogContent => _currentPage == 2 ? widget.terms : widget.policy;
|
||||
|
||||
Widget _buildScrollableContent() {
|
||||
return Container(
|
||||
@ -87,7 +85,7 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.all(25),
|
||||
child: Html(
|
||||
data: "$_dialogContent $staticText",
|
||||
data: _dialogContent,
|
||||
onLinkTap: (url, attributes, element) async {
|
||||
if (url != null) {
|
||||
final uri = Uri.parse(url);
|
||||
|
@ -24,7 +24,7 @@ class HomeWebPage extends StatelessWidget {
|
||||
listener: (BuildContext context, state) {
|
||||
if (state is HomeInitial) {
|
||||
if (homeBloc.user!.hasAcceptedWebAgreement == false) {
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
|
@ -42,9 +42,7 @@ class RolesUserModel {
|
||||
invitedBy:
|
||||
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
||||
phoneNumber: json['phoneNumber'],
|
||||
jobTitle: json['jobTitle'] == null || json['jobTitle'] == " "
|
||||
? "_"
|
||||
: json['jobTitle'],
|
||||
jobTitle: json['jobTitle'] ?? "-",
|
||||
createdDate: json['createdDate'],
|
||||
createdTime: json['createdTime'],
|
||||
);
|
||||
|
@ -79,14 +79,13 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
|
||||
List<TreeNode> updatedCommunities = [];
|
||||
List<TreeNode> spacesNodes = [];
|
||||
List<String> communityIds = [];
|
||||
|
||||
_onLoadCommunityAndSpaces(
|
||||
LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
List<CommunityModel> communities =
|
||||
await CommunitySpaceManagementApi().fetchCommunities();
|
||||
communityIds = communities.map((community) => community.uuid).toList();
|
||||
updatedCommunities = await Future.wait(
|
||||
communities.map((community) async {
|
||||
List<SpaceModel> spaces =
|
||||
@ -103,6 +102,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
}).toList(),
|
||||
);
|
||||
emit(const SpacesLoadedState());
|
||||
return updatedCommunities;
|
||||
} catch (e) {
|
||||
emit(ErrorState('Error loading communities and spaces: $e'));
|
||||
}
|
||||
@ -177,6 +177,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
roles = await UserPermissionApi().fetchRoles();
|
||||
// add(PermissionEvent(roleUuid: roles.first.uuid));
|
||||
emit(RolePermissionInitial());
|
||||
} catch (e) {
|
||||
emit(ErrorState('Error loading communities and spaces: $e'));
|
||||
@ -207,13 +208,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
return anyMatch;
|
||||
}
|
||||
|
||||
void _sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
||||
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities)
|
||||
.where((id) => !communityIds.contains(id))
|
||||
.toList();
|
||||
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
||||
bool res = await UserPermissionApi().sendInviteUser(
|
||||
email: emailController.text,
|
||||
firstName: firstNameController.text,
|
||||
@ -221,10 +219,9 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
lastName: lastNameController.text,
|
||||
phoneNumber: phoneController.text,
|
||||
roleUuid: roleSelected,
|
||||
spaceUuids: selectedIds,
|
||||
spaceUuids: selectedIds,
|
||||
);
|
||||
|
||||
if (res) {
|
||||
if (res == true) {
|
||||
showCustomDialog(
|
||||
barrierDismissible: false,
|
||||
context: event.context,
|
||||
@ -251,14 +248,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities)
|
||||
.where((id) => !communityIds.contains(id))
|
||||
.toList();
|
||||
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
||||
bool res = await UserPermissionApi().editInviteUser(
|
||||
userId: event.userId,
|
||||
firstName: firstNameController.text,
|
||||
|
@ -218,7 +218,7 @@ class BasicsView extends StatelessWidget {
|
||||
if (_blocRole.checkEmailValid != "Valid email") {
|
||||
return _blocRole.checkEmailValid;
|
||||
}
|
||||
// return null;
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -81,7 +81,7 @@ Future<void> showPopUpFilterMenu({
|
||||
),
|
||||
const Divider(),
|
||||
const Text(
|
||||
"Filter by ",
|
||||
"Filter by Status",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
Container(
|
||||
|
@ -40,7 +40,9 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
roleTypes.clear();
|
||||
jobTitle.clear();
|
||||
createdBy.clear();
|
||||
// deActivate.clear();
|
||||
users = await UserPermissionApi().fetchUsers();
|
||||
|
||||
users.sort((a, b) {
|
||||
final dateA = _parseDateTime(a.createdDate);
|
||||
final dateB = _parseDateTime(b.createdDate);
|
||||
@ -55,12 +57,15 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
for (var user in users) {
|
||||
createdBy.add(user.invitedBy.toString());
|
||||
}
|
||||
// for (var user in users) {
|
||||
// deActivate.add(user.status.toString());
|
||||
// }
|
||||
initialUsers = List.from(users);
|
||||
roleTypes = roleTypes.toSet().toList();
|
||||
jobTitle = jobTitle.toSet().toList();
|
||||
createdBy = createdBy.toSet().toList();
|
||||
// deActivate = deActivate.toSet().toList();
|
||||
_handlePageChange(ChangePage(1), emit);
|
||||
add(ChangePage(currentPage));
|
||||
emit(UsersLoadedState(users: users));
|
||||
} catch (e) {
|
||||
emit(ErrorState(e.toString()));
|
||||
@ -120,10 +125,6 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
void _toggleSortUsersByNameAsc(
|
||||
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
||||
selectedRoles.clear();
|
||||
selectedJobTitles.clear();
|
||||
selectedCreatedBy.clear();
|
||||
selectedStatuses.clear();
|
||||
if (currentSortOrder == "Asc") {
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
@ -142,16 +143,13 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
void _toggleSortUsersByNameDesc(
|
||||
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
||||
selectedRoles.clear();
|
||||
selectedJobTitles.clear();
|
||||
selectedCreatedBy.clear();
|
||||
selectedStatuses.clear();
|
||||
if (currentSortOrder == "Desc") {
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
users = List.from(initialUsers);
|
||||
users = List.from(initialUsers); // Reset to saved initial state
|
||||
emit(UsersLoadedState(users: users));
|
||||
} else {
|
||||
// Sort descending
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "Desc";
|
||||
users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||
@ -161,10 +159,6 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
void _toggleSortUsersByDateNewestToOldest(
|
||||
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
||||
selectedRoles.clear();
|
||||
selectedJobTitles.clear();
|
||||
selectedCreatedBy.clear();
|
||||
selectedStatuses.clear();
|
||||
if (currentSortOrderDate == "NewestToOldest") {
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
@ -185,10 +179,6 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
void _toggleSortUsersByDateOldestToNewest(
|
||||
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
||||
selectedRoles.clear();
|
||||
selectedJobTitles.clear();
|
||||
selectedCreatedBy.clear();
|
||||
selectedStatuses.clear();
|
||||
if (currentSortOrderDate == "OldestToNewest") {
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
@ -347,20 +337,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
final filteredUsers = initialUsers.where((user) {
|
||||
if (selectedStatuses.isEmpty) return true;
|
||||
|
||||
return selectedStatuses.any((status) {
|
||||
final userStatus = user.status?.toLowerCase() ?? '';
|
||||
switch (status.toLowerCase()) {
|
||||
case 'active':
|
||||
return user.isEnabled == true && userStatus != 'invited';
|
||||
case 'disabled':
|
||||
return user.isEnabled == false;
|
||||
case 'invited':
|
||||
return userStatus == 'invited';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return selectedStatuses.contains(user.status);
|
||||
}).toList();
|
||||
if (event.sortOrder == "Asc") {
|
||||
currentSortOrder = "Asc";
|
||||
@ -374,11 +351,9 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
} else {
|
||||
currentSortOrder = "";
|
||||
}
|
||||
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
}
|
||||
|
||||
|
||||
void _resetAllFilters(Emitter<UserTableState> emit) {
|
||||
selectedRoles.clear();
|
||||
selectedJobTitles.clear();
|
||||
|
@ -12,7 +12,7 @@ Future<void> showDateFilterMenu({
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
final RelativeRect position = RelativeRect.fromRect(
|
||||
Rect.fromLTRB(
|
||||
overlay.size.width / 3,
|
||||
overlay.size.width / 2,
|
||||
240,
|
||||
0,
|
||||
overlay.size.height,
|
||||
@ -40,6 +40,7 @@ Future<void> showDateFilterMenu({
|
||||
),
|
||||
title: Text(
|
||||
"Sort from newest to oldest",
|
||||
// style: context.textTheme.bodyMedium,
|
||||
style: TextStyle(
|
||||
color: isSelected == "NewestToOldest"
|
||||
? Colors.black
|
||||
@ -64,5 +65,9 @@ Future<void> showDateFilterMenu({
|
||||
),
|
||||
),
|
||||
],
|
||||
).then((value) {});
|
||||
).then((value) {
|
||||
// setState(() {
|
||||
// _isDropdownOpen = false;
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ Future<void> showDeActivateFilterMenu({
|
||||
),
|
||||
title: Text(
|
||||
"Sort A to Z",
|
||||
// style: context.textTheme.bodyMedium,
|
||||
style: TextStyle(
|
||||
color: isSelected == "NewestToOldest"
|
||||
? Colors.black
|
||||
@ -64,5 +65,9 @@ Future<void> showDeActivateFilterMenu({
|
||||
),
|
||||
),
|
||||
],
|
||||
).then((value) {});
|
||||
).then((value) {
|
||||
// setState(() {
|
||||
// _isDropdownOpen = false;
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ Future<void> showNameMenu({
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
final RelativeRect position = RelativeRect.fromRect(
|
||||
Rect.fromLTRB(
|
||||
overlay.size.width / 35,
|
||||
overlay.size.width / 25,
|
||||
240,
|
||||
0,
|
||||
overlay.size.height,
|
||||
@ -40,6 +40,7 @@ Future<void> showNameMenu({
|
||||
),
|
||||
title: Text(
|
||||
"Sort A to Z",
|
||||
// style: context.textTheme.bodyMedium,
|
||||
style: TextStyle(
|
||||
color: isSelected == "Asc" ? Colors.black : Colors.blueGrey),
|
||||
),
|
||||
@ -60,5 +61,9 @@ Future<void> showNameMenu({
|
||||
),
|
||||
),
|
||||
],
|
||||
).then((value) {});
|
||||
).then((value) {
|
||||
// setState(() {
|
||||
// _isDropdownOpen = false;
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
@ -1,264 +1,260 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class _HeaderColumn extends StatelessWidget {
|
||||
final String title;
|
||||
final double width;
|
||||
final bool showFilter;
|
||||
final VoidCallback? onFilter;
|
||||
final Function(double) onResize;
|
||||
|
||||
const _HeaderColumn({
|
||||
required this.title,
|
||||
required this.width,
|
||||
required this.showFilter,
|
||||
required this.onResize,
|
||||
this.onFilter,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.resizeColumn,
|
||||
child: GestureDetector(
|
||||
onHorizontalDragUpdate: (details) => onResize(details.delta.dx),
|
||||
child: Container(
|
||||
width: width,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(right: BorderSide(color: ColorsManager.boxDivider)),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (showFilter)
|
||||
IconButton(
|
||||
icon: SvgPicture.asset(Assets.filterTableIcon),
|
||||
onPressed: onFilter,
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TableRow extends StatelessWidget {
|
||||
final List<Widget> cells;
|
||||
final List<double> columnWidths;
|
||||
final bool isLast;
|
||||
|
||||
const _TableRow({
|
||||
required this.cells,
|
||||
required this.columnWidths,
|
||||
required this.isLast,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
for (int i = 0; i < cells.length; i++)
|
||||
Container(
|
||||
width: columnWidths[i],
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border(
|
||||
// right: BorderSide(color: ColorsManager.boxDivider),
|
||||
// ),
|
||||
// ),
|
||||
child: cells[i],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!isLast)
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
color: ColorsManager.boxDivider,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
//===========================================================================
|
||||
|
||||
class DynamicTableScreen extends StatefulWidget {
|
||||
final List<String> titles;
|
||||
final List<List<Widget>> rows;
|
||||
final void Function(int columnIndex)? onFilter;
|
||||
|
||||
const DynamicTableScreen({
|
||||
required this.titles,
|
||||
required this.rows,
|
||||
required this.onFilter,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
DynamicTableScreen(
|
||||
{required this.titles, required this.rows, required this.onFilter});
|
||||
|
||||
@override
|
||||
_DynamicTableScreenState createState() => _DynamicTableScreenState();
|
||||
}
|
||||
|
||||
class _DynamicTableScreenState extends State<DynamicTableScreen> {
|
||||
class _DynamicTableScreenState extends State<DynamicTableScreen>
|
||||
with WidgetsBindingObserver {
|
||||
late List<double> columnWidths;
|
||||
final double _minColumnWidth = 100.0;
|
||||
final double _maxColumnWidth = 300.0;
|
||||
final double _dividerWidth = 1.0;
|
||||
double _lastAvailableWidth = 0;
|
||||
late double totalWidth;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
columnWidths = List.filled(widget.titles.length, _minColumnWidth);
|
||||
columnWidths = List<double>.filled(widget.titles.length, 150.0);
|
||||
totalWidth = columnWidths.reduce((a, b) => a + b);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
void _handleColumnResize(int index, double delta) {
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
super.didChangeMetrics();
|
||||
final newScreenWidth = MediaQuery.of(context).size.width;
|
||||
setState(() {
|
||||
double newWidth = columnWidths[index] + delta;
|
||||
newWidth = newWidth.clamp(_minColumnWidth, _maxColumnWidth);
|
||||
double actualDelta = newWidth - columnWidths[index];
|
||||
if (actualDelta == 0) return;
|
||||
|
||||
int nextIndex = (index + 1) % columnWidths.length;
|
||||
columnWidths[index] = newWidth;
|
||||
columnWidths[nextIndex] = (columnWidths[nextIndex] - actualDelta)
|
||||
.clamp(_minColumnWidth, _maxColumnWidth);
|
||||
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
||||
if (index == 1) {
|
||||
return newScreenWidth *
|
||||
0.12; // 20% of screen width for the second column
|
||||
} else if (index == 9) {
|
||||
return newScreenWidth *
|
||||
0.1; // 25% of screen width for the tenth column
|
||||
}
|
||||
return newScreenWidth *
|
||||
0.09; // Default to 10% of screen width for other columns
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
topRight: Radius.circular(15),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
for (int i = 0; i < widget.titles.length; i++)
|
||||
_HeaderColumn(
|
||||
title: widget.titles[i],
|
||||
width: columnWidths[i],
|
||||
showFilter: i != 1 && i != 9 && i != 8 && i != 5,
|
||||
onFilter: () => widget.onFilter?.call(i),
|
||||
onResize: (delta) => _handleColumnResize(i, delta),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
if (widget.rows.isEmpty) {
|
||||
return SizedBox(
|
||||
height: 300,
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyTable),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
'No Users',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(15),
|
||||
bottomRight: Radius.circular(15),
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
for (int rowIndex = 0; rowIndex < widget.rows.length; rowIndex++)
|
||||
_TableRow(
|
||||
cells: widget.rows[rowIndex],
|
||||
columnWidths: columnWidths,
|
||||
isLast: rowIndex == widget.rows.length - 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final availableWidth = constraints.maxWidth;
|
||||
final totalDividersWidth = (widget.titles.length - 1) * _dividerWidth;
|
||||
|
||||
if (_lastAvailableWidth != availableWidth) {
|
||||
final equalWidth =
|
||||
(availableWidth - totalDividersWidth) / widget.titles.length;
|
||||
final clampedWidth =
|
||||
equalWidth.clamp(_minColumnWidth, _maxColumnWidth);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
columnWidths = List.filled(widget.titles.length, clampedWidth);
|
||||
_lastAvailableWidth = availableWidth;
|
||||
});
|
||||
});
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
if (columnWidths.every((width) => width == screenWidth * 7)) {
|
||||
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
||||
if (index == 1) {
|
||||
return screenWidth * 0.11;
|
||||
} else if (index == 9) {
|
||||
return screenWidth * 0.1;
|
||||
}
|
||||
|
||||
final totalTableWidth =
|
||||
columnWidths.fold(0.0, (sum, w) => sum + w) + totalDividersWidth;
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Container(
|
||||
width: totalTableWidth,
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
_buildBody(),
|
||||
],
|
||||
),
|
||||
return screenWidth * 0.09;
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
clipBehavior: Clip.none,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Container(
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
||||
child: FittedBox(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: totalWidth,
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
topRight: Radius.circular(15))),
|
||||
child: Row(
|
||||
children: List.generate(widget.titles.length, (index) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
FittedBox(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(left: 5, right: 5),
|
||||
width: columnWidths[index],
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
child: Text(
|
||||
widget.titles[index],
|
||||
maxLines: 2,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (index != 1 &&
|
||||
index != 9 &&
|
||||
index != 8 &&
|
||||
index != 5)
|
||||
FittedBox(
|
||||
child: IconButton(
|
||||
icon: SvgPicture.asset(
|
||||
Assets.filterTableIcon,
|
||||
fit: BoxFit.none,
|
||||
),
|
||||
onPressed: () {
|
||||
if (widget.onFilter != null) {
|
||||
widget.onFilter!(index);
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onHorizontalDragUpdate: (details) {
|
||||
setState(() {
|
||||
columnWidths[index] =
|
||||
(columnWidths[index] + details.delta.dx)
|
||||
.clamp(150.0, 300.0);
|
||||
totalWidth = columnWidths.reduce((a, b) => a + b);
|
||||
});
|
||||
},
|
||||
child: MouseRegion(
|
||||
cursor: SystemMouseCursors.resizeColumn,
|
||||
child: Container(
|
||||
color: Colors.green,
|
||||
child: Container(
|
||||
color: ColorsManager.boxDivider,
|
||||
width: 1,
|
||||
height: 50,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
widget.rows.isEmpty
|
||||
? SizedBox(
|
||||
height: MediaQuery.of(context).size.height / 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyTable),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
const Text(
|
||||
'No Users',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Center(
|
||||
child: Container(
|
||||
width: totalWidth,
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(15),
|
||||
bottomRight: Radius.circular(15))),
|
||||
child: ListView.builder(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.rows.length,
|
||||
itemBuilder: (context, rowIndex) {
|
||||
if (columnWidths.every((width) => width == 120.0)) {
|
||||
columnWidths = List<double>.generate(
|
||||
widget.titles.length, (index) {
|
||||
if (index == 1) {
|
||||
return screenWidth * 0.11;
|
||||
} else if (index == 9) {
|
||||
return screenWidth * 0.2;
|
||||
}
|
||||
return screenWidth * 0.11;
|
||||
});
|
||||
setState(() {});
|
||||
}
|
||||
final row = widget.rows[rowIndex];
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 5, top: 10, right: 5, bottom: 10),
|
||||
child: Row(
|
||||
children:
|
||||
List.generate(row.length, (index) {
|
||||
return SizedBox(
|
||||
width: columnWidths[index],
|
||||
child: SizedBox(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 15, right: 10),
|
||||
child: row[index],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (rowIndex < widget.rows.length - 1)
|
||||
Row(
|
||||
children: List.generate(
|
||||
widget.titles.length, (index) {
|
||||
return SizedBox(
|
||||
width: columnWidths[index],
|
||||
child: const Divider(
|
||||
color: ColorsManager.boxDivider,
|
||||
thickness: 1,
|
||||
height: 1,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ class UsersPage extends StatelessWidget {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||
if (state is UsersLoadingState) {
|
||||
_blocRole.add(ChangePage(_blocRole.currentPage));
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is UsersLoadedState) {
|
||||
return Padding(
|
||||
@ -214,7 +215,7 @@ class UsersPage extends StatelessWidget {
|
||||
|
||||
showPopUpFilterMenu(
|
||||
position: RelativeRect.fromLTRB(
|
||||
overlay.size.width / 5.3,
|
||||
overlay.size.width / 4,
|
||||
240,
|
||||
overlay.size.width / 4,
|
||||
0,
|
||||
@ -224,7 +225,6 @@ class UsersPage extends StatelessWidget {
|
||||
checkboxStates: checkboxStates,
|
||||
isSelected: _blocRole.currentSortOrder,
|
||||
onOkPressed: () {
|
||||
searchController.clear();
|
||||
_blocRole.add(FilterClearEvent());
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
@ -265,7 +265,6 @@ class UsersPage extends StatelessWidget {
|
||||
checkboxStates: checkboxStates,
|
||||
isSelected: _blocRole.currentSortOrder,
|
||||
onOkPressed: () {
|
||||
searchController.clear();
|
||||
_blocRole.add(FilterClearEvent());
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
@ -321,7 +320,6 @@ class UsersPage extends StatelessWidget {
|
||||
checkboxStates: checkboxStates,
|
||||
isSelected: _blocRole.currentSortOrder,
|
||||
onOkPressed: () {
|
||||
searchController.clear();
|
||||
_blocRole.add(FilterClearEvent());
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
@ -345,7 +343,6 @@ class UsersPage extends StatelessWidget {
|
||||
for (var item in _blocRole.status)
|
||||
item: _blocRole.selectedStatuses.contains(item),
|
||||
};
|
||||
|
||||
final RenderBox overlay = Overlay.of(context)
|
||||
.context
|
||||
.findRenderObject() as RenderBox;
|
||||
@ -353,7 +350,7 @@ class UsersPage extends StatelessWidget {
|
||||
position: RelativeRect.fromLTRB(
|
||||
overlay.size.width / 0,
|
||||
240,
|
||||
overlay.size.width / 5,
|
||||
overlay.size.width / 4,
|
||||
0,
|
||||
),
|
||||
list: _blocRole.status,
|
||||
@ -361,8 +358,8 @@ class UsersPage extends StatelessWidget {
|
||||
checkboxStates: checkboxStates,
|
||||
isSelected: _blocRole.currentSortOrder,
|
||||
onOkPressed: () {
|
||||
searchController.clear();
|
||||
_blocRole.add(FilterClearEvent());
|
||||
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key)
|
||||
@ -413,7 +410,7 @@ class UsersPage extends StatelessWidget {
|
||||
return [
|
||||
Text('${user.firstName} ${user.lastName}'),
|
||||
Text(user.email),
|
||||
Text(user.jobTitle),
|
||||
Text(user.jobTitle ?? '-'),
|
||||
Text(user.roleType ?? ''),
|
||||
Text(user.createdDate ?? ''),
|
||||
Text(user.createdTime ?? ''),
|
||||
@ -430,6 +427,11 @@ class UsersPage extends StatelessWidget {
|
||||
userId: user.uuid,
|
||||
onTap: user.status != "invited"
|
||||
? () {
|
||||
// final newStatus = user.status == 'active'
|
||||
// ? 'disabled'
|
||||
// : user.status == 'disabled'
|
||||
// ? 'invited'
|
||||
// : 'active';
|
||||
context.read<UserTableBloc>().add(
|
||||
ChangeUserStatus(
|
||||
userId: user.uuid,
|
||||
@ -441,6 +443,10 @@ class UsersPage extends StatelessWidget {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
// actionButton(
|
||||
// title: "Activity Log",
|
||||
// onTap: () {},
|
||||
// ),
|
||||
actionButton(
|
||||
title: "Edit",
|
||||
onTap: () {
|
||||
@ -481,7 +487,9 @@ class UsersPage extends StatelessWidget {
|
||||
},
|
||||
).then((v) {
|
||||
if (v != null) {
|
||||
_blocRole.add(const GetUsers());
|
||||
if (v != null) {
|
||||
_blocRole.add(const GetUsers());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -33,64 +33,55 @@ class _RoutinesViewState extends State<RoutinesView> {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
// SideSpacesView(
|
||||
// onSelectAction: (String communityId, String spaceId) {
|
||||
// // context.read<RoutineBloc>()
|
||||
// // ..add(LoadScenes(spaceId, communityId))
|
||||
// // ..add(LoadAutomation(spaceId));
|
||||
// },
|
||||
// )
|
||||
SpaceTreeView(
|
||||
child: SpaceTreeView(
|
||||
onSelect: () {},
|
||||
)),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Create New Routines",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
RoutineViewCard(
|
||||
onTap: () {
|
||||
if (context.read<SpaceTreeBloc>().selectedCommunityId.isNotEmpty &&
|
||||
context.read<SpaceTreeBloc>().selectedSpaceId.isNotEmpty) {
|
||||
context.read<RoutineBloc>().add(
|
||||
(ResetRoutineState()),
|
||||
);
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||
);
|
||||
} else {
|
||||
CustomSnackBar.redSnackBar('Please select a space');
|
||||
}
|
||||
},
|
||||
icon: Icons.add,
|
||||
textString: '',
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
const Expanded(child: FetchRoutineScenesAutomation()),
|
||||
],
|
||||
),
|
||||
],
|
||||
flex: 4,
|
||||
child: ListView(children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Create New Routines",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
RoutineViewCard(
|
||||
onTap: () {
|
||||
if (context.read<SpaceTreeBloc>().selectedCommunityId.isNotEmpty &&
|
||||
context.read<SpaceTreeBloc>().selectedSpaceId.isNotEmpty) {
|
||||
context.read<RoutineBloc>().add(
|
||||
(ResetRoutineState()),
|
||||
);
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(createRoutineView: true),
|
||||
);
|
||||
} else {
|
||||
CustomSnackBar.redSnackBar('Please select a space');
|
||||
}
|
||||
},
|
||||
icon: Icons.add,
|
||||
textString: '',
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
const Expanded(child: FetchRoutineScenesAutomation()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -10,10 +10,23 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class SpaceTreeView extends StatelessWidget {
|
||||
class SpaceTreeView extends StatefulWidget {
|
||||
final Function onSelect;
|
||||
const SpaceTreeView({required this.onSelect, super.key});
|
||||
|
||||
@override
|
||||
State<SpaceTreeView> createState() => _SpaceTreeViewState();
|
||||
}
|
||||
|
||||
class _SpaceTreeViewState extends State<SpaceTreeView> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||
@ -21,7 +34,6 @@ class SpaceTreeView extends StatelessWidget {
|
||||
return Container(
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
decoration: subSectionContainerDecoration,
|
||||
// padding: const EdgeInsets.all(16.0),
|
||||
child: state is SpaceTreeLoadingState
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Column(
|
||||
@ -33,67 +45,150 @@ class SpaceTreeView extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: list.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
'No results found',
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.lightGrayColor, // Gray when not selected
|
||||
fontWeight: FontWeight.w400,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.sizeOf(context).width * 0.5,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: list.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
'No results found',
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Scrollbar(
|
||||
scrollbarOrientation: ScrollbarOrientation.left,
|
||||
thumbVisibility: true,
|
||||
controller: _scrollController,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
shrinkWrap: true,
|
||||
children: list
|
||||
.map(
|
||||
(community) => CustomExpansionTileSpaceTree(
|
||||
title: community.name,
|
||||
isSelected: state.selectedCommunities
|
||||
.contains(community.uuid),
|
||||
isSoldCheck: state.selectedCommunities
|
||||
.contains(community.uuid),
|
||||
onExpansionChanged: () {
|
||||
context
|
||||
.read<SpaceTreeBloc>()
|
||||
.add(OnCommunityExpanded(community.uuid));
|
||||
},
|
||||
isExpanded: state.expandedCommunities
|
||||
.contains(community.uuid),
|
||||
onItemSelected: () {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnCommunitySelected(
|
||||
community.uuid, community.spaces));
|
||||
widget.onSelect();
|
||||
},
|
||||
children: community.spaces.map((space) {
|
||||
return CustomExpansionTileSpaceTree(
|
||||
title: space.name,
|
||||
isExpanded:
|
||||
state.expandedSpaces.contains(space.uuid),
|
||||
onItemSelected: () {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceSelected(community.uuid,
|
||||
space.uuid ?? '', space.children));
|
||||
widget.onSelect();
|
||||
},
|
||||
onExpansionChanged: () {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceExpanded(
|
||||
community.uuid, space.uuid ?? ''));
|
||||
},
|
||||
isSelected:
|
||||
state.selectedSpaces.contains(space.uuid) ||
|
||||
state.soldCheck.contains(space.uuid),
|
||||
isSoldCheck: state.soldCheck.contains(space.uuid),
|
||||
children: _buildNestedSpaces(
|
||||
context, state, space, community.uuid),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: ListView(
|
||||
shrinkWrap: true,
|
||||
children: list
|
||||
.map(
|
||||
(community) => CustomExpansionTileSpaceTree(
|
||||
title: community.name,
|
||||
isSelected:
|
||||
state.selectedCommunities.contains(community.uuid),
|
||||
isSoldCheck:
|
||||
state.selectedCommunities.contains(community.uuid),
|
||||
onExpansionChanged: () {
|
||||
context
|
||||
.read<SpaceTreeBloc>()
|
||||
.add(OnCommunityExpanded(community.uuid));
|
||||
},
|
||||
isExpanded:
|
||||
state.expandedCommunities.contains(community.uuid),
|
||||
onItemSelected: () {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnCommunitySelected(community.uuid, community.spaces));
|
||||
|
||||
onSelect();
|
||||
},
|
||||
children: community.spaces.map((space) {
|
||||
return CustomExpansionTileSpaceTree(
|
||||
title: space.name,
|
||||
isExpanded: state.expandedSpaces.contains(space.uuid),
|
||||
onItemSelected: () {
|
||||
context.read<SpaceTreeBloc>().add(OnSpaceSelected(
|
||||
community.uuid, space.uuid ?? '', space.children));
|
||||
onSelect();
|
||||
},
|
||||
onExpansionChanged: () {
|
||||
context.read<SpaceTreeBloc>().add(
|
||||
OnSpaceExpanded(community.uuid, space.uuid ?? ''));
|
||||
},
|
||||
isSelected: state.selectedSpaces.contains(space.uuid) ||
|
||||
state.soldCheck.contains(space.uuid),
|
||||
isSoldCheck: state.soldCheck.contains(space.uuid),
|
||||
children: _buildNestedSpaces(
|
||||
context, state, space, community.uuid),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Expanded(
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: list.isEmpty
|
||||
// ? Center(
|
||||
// child: Text(
|
||||
// 'No results found',
|
||||
// style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
// color: ColorsManager.lightGrayColor, // Gray when not selected
|
||||
// fontWeight: FontWeight.w400,
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : ListView(
|
||||
// shrinkWrap: true,
|
||||
// children: list
|
||||
// .map(
|
||||
// (community) => CustomExpansionTileSpaceTree(
|
||||
// title: community.name,
|
||||
// isSelected:
|
||||
// state.selectedCommunities.contains(community.uuid),
|
||||
// isSoldCheck:
|
||||
// state.selectedCommunities.contains(community.uuid),
|
||||
// onExpansionChanged: () {
|
||||
// context
|
||||
// .read<SpaceTreeBloc>()
|
||||
// .add(OnCommunityExpanded(community.uuid));
|
||||
// },
|
||||
// isExpanded:
|
||||
// state.expandedCommunities.contains(community.uuid),
|
||||
// onItemSelected: () {
|
||||
// context.read<SpaceTreeBloc>().add(
|
||||
// OnCommunitySelected(community.uuid, community.spaces));
|
||||
|
||||
// onSelect();
|
||||
// },
|
||||
// children: community.spaces.map((space) {
|
||||
// return CustomExpansionTileSpaceTree(
|
||||
// title: space.name,
|
||||
// isExpanded: state.expandedSpaces.contains(space.uuid),
|
||||
// onItemSelected: () {
|
||||
// context.read<SpaceTreeBloc>().add(OnSpaceSelected(
|
||||
// community.uuid, space.uuid ?? '', space.children));
|
||||
// onSelect();
|
||||
// },
|
||||
// onExpansionChanged: () {
|
||||
// context.read<SpaceTreeBloc>().add(
|
||||
// OnSpaceExpanded(community.uuid, space.uuid ?? ''));
|
||||
// },
|
||||
// isSelected: state.selectedSpaces.contains(space.uuid) ||
|
||||
// state.soldCheck.contains(space.uuid),
|
||||
// isSoldCheck: state.soldCheck.contains(space.uuid),
|
||||
// children: _buildNestedSpaces(
|
||||
// context, state, space, community.uuid),
|
||||
// );
|
||||
// }).toList(),
|
||||
// ),
|
||||
// )
|
||||
// .toList(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -113,7 +208,7 @@ class SpaceTreeView extends StatelessWidget {
|
||||
context
|
||||
.read<SpaceTreeBloc>()
|
||||
.add(OnSpaceSelected(communityId, child.uuid ?? '', child.children));
|
||||
onSelect();
|
||||
widget.onSelect();
|
||||
},
|
||||
onExpansionChanged: () {
|
||||
context.read<SpaceTreeBloc>().add(OnSpaceExpanded(communityId, child.uuid ?? ''));
|
||||
|
@ -87,7 +87,6 @@ class SpaceManagementBloc
|
||||
prevSpaceModels = List<SpaceTemplateModel>.from(
|
||||
(previousState as dynamic).spaceModels ?? [],
|
||||
);
|
||||
allSpaces.addAll(prevSpaceModels);
|
||||
}
|
||||
|
||||
if (prevSpaceModels.isEmpty) {
|
||||
@ -180,7 +179,6 @@ class SpaceManagementBloc
|
||||
final updatedCommunities =
|
||||
await Future.wait(communities.map((community) async {
|
||||
final spaces = await _fetchSpacesForCommunity(community.uuid);
|
||||
|
||||
return CommunityModel(
|
||||
uuid: community.uuid,
|
||||
createdAt: community.createdAt,
|
||||
|
@ -95,9 +95,6 @@ class SpaceModel {
|
||||
icon: json['icon'] ?? Assets.location,
|
||||
position: Offset(json['x'] ?? 0, json['y'] ?? 0),
|
||||
isHovered: false,
|
||||
spaceModel: json['spaceModel'] != null
|
||||
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
||||
: null,
|
||||
tags: (json['tags'] as List<dynamic>?)
|
||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
||||
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
||||
|
@ -64,11 +64,11 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
);
|
||||
} else if (state is SpaceModelLoaded) {
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: true,
|
||||
);
|
||||
communities: state.communities,
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: true,
|
||||
);
|
||||
} else if (state is SpaceManagementError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
}
|
||||
|
@ -22,9 +22,6 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_li
|
||||
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_container_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/connection_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/space_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
@ -133,7 +130,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
communities: widget.communities,
|
||||
communityName: widget.selectedCommunity?.name,
|
||||
community: widget.selectedCommunity,
|
||||
isSave: SpaceHelper.isSave(spaces),
|
||||
isSave: isSave(spaces),
|
||||
isEditingName: isEditingName,
|
||||
nameController: _nameController,
|
||||
onSave: _saveSpaces,
|
||||
@ -178,8 +175,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
children: [
|
||||
for (var connection in connections)
|
||||
Opacity(
|
||||
opacity: ConnectionHelper.isHighlightedConnection(
|
||||
connection, widget.selectedSpace)
|
||||
opacity: _isHighlightedConnection(connection)
|
||||
? 1.0
|
||||
: 0.3, // Adjust opacity
|
||||
child: CustomPaint(
|
||||
@ -199,6 +195,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
screenSize,
|
||||
position:
|
||||
spaces[index].position + newPosition,
|
||||
|
||||
parentIndex: index,
|
||||
direction: direction,
|
||||
);
|
||||
@ -212,8 +209,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
},
|
||||
buildSpaceContainer: (int index) {
|
||||
final bool isHighlighted =
|
||||
SpaceHelper.isHighlightedSpace(
|
||||
spaces[index], widget.selectedSpace);
|
||||
_isHighlightedSpace(spaces[index]);
|
||||
|
||||
return Opacity(
|
||||
opacity: isHighlighted ? 1.0 : 0.3,
|
||||
@ -299,8 +295,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
spaceModels: widget.spaceModels,
|
||||
allTags:
|
||||
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
allTags: _getAllTagValues(spaces),
|
||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
@ -311,7 +306,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
setState(() {
|
||||
// Set the first space in the center or use passed position
|
||||
Offset centerPosition =
|
||||
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
position ?? _getCenterPosition(screenSize);
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
name: name,
|
||||
icon: icon,
|
||||
@ -356,15 +351,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
spaceModels: widget.spaceModels,
|
||||
name: widget.selectedSpace!.name,
|
||||
icon: widget.selectedSpace!.icon,
|
||||
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||
widget.selectedSpace?.parent?.internalId, spaces),
|
||||
editSpace: widget.selectedSpace,
|
||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||
tags: widget.selectedSpace?.tags,
|
||||
subspaces: widget.selectedSpace?.subspaces,
|
||||
isEdit: true,
|
||||
allTags: TagHelper.getAllTagValues(
|
||||
widget.communities, widget.spaceModels),
|
||||
allTags: _getAllTagValues(spaces),
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
@ -383,15 +374,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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.tags = tags;
|
||||
space.name = name;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
key: Key(widget.selectedSpace!.name),
|
||||
@ -470,6 +452,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
}).toList();
|
||||
|
||||
if (spacesToSave.isEmpty) {
|
||||
debugPrint("No new or modified spaces to save.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -533,6 +516,17 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
);
|
||||
}
|
||||
|
||||
bool _isHighlightedSpace(SpaceModel space) {
|
||||
final selectedSpace = widget.selectedSpace;
|
||||
if (selectedSpace == null) return true;
|
||||
|
||||
return space == selectedSpace ||
|
||||
selectedSpace.parent?.internalId == space.internalId ||
|
||||
selectedSpace.children
|
||||
?.any((child) => child.internalId == space.internalId) ==
|
||||
true;
|
||||
}
|
||||
|
||||
void _deselectSpace(BuildContext context) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
@ -540,6 +534,28 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
);
|
||||
}
|
||||
|
||||
bool _isHighlightedConnection(Connection connection) {
|
||||
if (widget.selectedSpace == null) return true;
|
||||
|
||||
return connection.startSpace == widget.selectedSpace ||
|
||||
connection.endSpace == widget.selectedSpace;
|
||||
}
|
||||
|
||||
Offset _getCenterPosition(Size screenSize) {
|
||||
return Offset(
|
||||
screenSize.width / 2 - 260,
|
||||
screenSize.height / 2 - 200,
|
||||
);
|
||||
}
|
||||
|
||||
bool isSave(List<SpaceModel> spaces) {
|
||||
return spaces.isNotEmpty &&
|
||||
spaces.any((space) =>
|
||||
space.status == SpaceStatus.newSpace ||
|
||||
space.status == SpaceStatus.modified ||
|
||||
space.status == SpaceStatus.deleted);
|
||||
}
|
||||
|
||||
void _onDuplicate(BuildContext parentContext) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
@ -625,10 +641,17 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
const double horizontalGap = 200.0;
|
||||
const double verticalGap = 100.0;
|
||||
|
||||
final Map<String, int> nameCounters = {};
|
||||
|
||||
String _generateCopyName(String originalName) {
|
||||
final baseName = originalName.replaceAll(RegExp(r'\(\d+\)$'), '').trim();
|
||||
nameCounters[baseName] = (nameCounters[baseName] ?? 0) + 1;
|
||||
return "$baseName(${nameCounters[baseName]})";
|
||||
}
|
||||
|
||||
SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition,
|
||||
SpaceModel? duplicatedParent) {
|
||||
Offset newPosition =
|
||||
Offset(parentPosition.dx + horizontalGap, original.position.dy);
|
||||
Offset newPosition = parentPosition + Offset(horizontalGap, 0);
|
||||
|
||||
while (spaces.any((s) =>
|
||||
(s.position - newPosition).distance < horizontalGap &&
|
||||
@ -636,18 +659,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
newPosition += Offset(horizontalGap, 0);
|
||||
}
|
||||
|
||||
final duplicatedName =
|
||||
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||
|
||||
final List<SubspaceModel>? duplicatedSubspaces;
|
||||
final List<Tag>? duplicatedTags;
|
||||
if (original.spaceModel != null) {
|
||||
duplicatedTags = [];
|
||||
duplicatedSubspaces = [];
|
||||
} else {
|
||||
duplicatedTags = original.tags;
|
||||
duplicatedSubspaces = original.subspaces;
|
||||
}
|
||||
final duplicatedName = _generateCopyName(original.name);
|
||||
|
||||
final duplicated = SpaceModel(
|
||||
name: duplicatedName,
|
||||
@ -658,8 +670,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
status: SpaceStatus.newSpace,
|
||||
parent: duplicatedParent,
|
||||
spaceModel: original.spaceModel,
|
||||
subspaces: duplicatedSubspaces,
|
||||
tags: duplicatedTags,
|
||||
subspaces: original.subspaces,
|
||||
tags: original.tags,
|
||||
);
|
||||
|
||||
originalToDuplicate[original] = duplicated;
|
||||
@ -672,7 +684,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
final newConnection = Connection(
|
||||
startSpace: duplicatedParent,
|
||||
endSpace: duplicated,
|
||||
direction: original.incomingConnection?.direction ?? 'down',
|
||||
direction: "down",
|
||||
);
|
||||
connections.add(newConnection);
|
||||
duplicated.incomingConnection = newConnection;
|
||||
@ -711,8 +723,10 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -733,4 +747,14 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ 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/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/helper/space_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
@ -41,7 +40,6 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
final List<SubspaceModel>? subspaces;
|
||||
final List<Tag>? tags;
|
||||
final List<String>? allTags;
|
||||
final SpaceTemplateModel? currentSpaceModel;
|
||||
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
@ -56,8 +54,7 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
this.selectedProducts = const [],
|
||||
this.spaceModels,
|
||||
this.subspaces,
|
||||
this.tags,
|
||||
this.currentSpaceModel});
|
||||
this.tags});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
@ -84,22 +81,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled =
|
||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
if (widget.currentSpaceModel != null) {
|
||||
subspaces = [];
|
||||
tags = [];
|
||||
} else {
|
||||
tags = widget.tags ?? [];
|
||||
subspaces = widget.subspaces ?? [];
|
||||
}
|
||||
selectedSpaceModel = widget.currentSpaceModel;
|
||||
tags = widget.tags ?? [];
|
||||
subspaces = widget.subspaces ?? [];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||
subspaces != null && subspaces!.isNotEmpty);
|
||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
return AlertDialog(
|
||||
title: widget.isEdit
|
||||
@ -178,7 +165,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (SpaceHelper.isNameConflict(value, widget.parentSpace, widget.editSpace)) {
|
||||
if (_isNameConflict(value)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
@ -245,14 +232,11 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () {
|
||||
isSpaceModelDisabled
|
||||
? null
|
||||
: _showLinkSpaceModelDialog(context);
|
||||
_showLinkSpaceModelDialog(context);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
child: const ButtonContentWidget(
|
||||
svgAssets: Assets.link,
|
||||
label: 'Link a space model',
|
||||
disabled: isSpaceModelDisabled,
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
@ -341,15 +325,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
overlayColor: ColorsManager.transparentColor,
|
||||
),
|
||||
onPressed: () async {
|
||||
isTagsAndSubspaceModelDisabled
|
||||
? null
|
||||
: _showSubSpaceDialog(context, enteredName,
|
||||
[], false, widget.products, subspaces);
|
||||
_showSubSpaceDialog(context, enteredName, [],
|
||||
false, widget.products, subspaces);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
child: const ButtonContentWidget(
|
||||
icon: Icons.add,
|
||||
label: 'Create Sub Space',
|
||||
disabled: isTagsAndSubspaceModelDisabled,
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
@ -476,8 +457,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
context: context,
|
||||
builder: (context) => AssignTagDialog(
|
||||
products: widget.products,
|
||||
subspaces: subspaces,
|
||||
allTags: widget.allTags,
|
||||
subspaces: widget.subspaces,
|
||||
addedProducts: TagHelper
|
||||
.createInitialSelectedProductsForTags(
|
||||
tags ?? [], subspaces),
|
||||
@ -503,22 +483,20 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
)
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
isTagsAndSubspaceModelDisabled
|
||||
? null
|
||||
: _showTagCreateDialog(
|
||||
context,
|
||||
enteredName,
|
||||
widget.isEdit,
|
||||
widget.products,
|
||||
);
|
||||
_showTagCreateDialog(
|
||||
context,
|
||||
enteredName,
|
||||
widget.isEdit,
|
||||
widget.products,
|
||||
subspaces,
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
child: ButtonContentWidget(
|
||||
child: const ButtonContentWidget(
|
||||
icon: Icons.add,
|
||||
label: 'Add Devices',
|
||||
disabled: isTagsAndSubspaceModelDisabled,
|
||||
))
|
||||
],
|
||||
),
|
||||
@ -592,7 +570,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
bool _isNameConflict(String value) {
|
||||
return (widget.parentSpace?.children.any((child) => child.name == value) ??
|
||||
false) ||
|
||||
(widget.parentSpace?.name == value) ||
|
||||
(widget.editSpace?.parent?.name == value) ||
|
||||
(widget.editSpace?.children.any((child) => child.name == value) ??
|
||||
false);
|
||||
}
|
||||
|
||||
void _showLinkSpaceModelDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -631,26 +617,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
products: products,
|
||||
existingSubSpaces: existingSubSpaces,
|
||||
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) {
|
||||
setState(() {
|
||||
subspaces = slectedSubspaces;
|
||||
tags?.addAll(tagsToAppendToSpace);
|
||||
selectedSpaceModel = null;
|
||||
});
|
||||
}
|
||||
@ -660,7 +629,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||
List<ProductModel>? products) {
|
||||
List<ProductModel>? products, List<SubspaceModel>? subspaces) {
|
||||
isEdit
|
||||
? showDialog(
|
||||
context: context,
|
||||
|
@ -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/services/space_model_mang_api.dart';
|
||||
|
||||
class LoadedSpaceView extends StatefulWidget {
|
||||
class LoadedSpaceView extends StatelessWidget {
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? selectedCommunity;
|
||||
final SpaceModel? selectedSpace;
|
||||
@ -26,73 +26,41 @@ class LoadedSpaceView extends StatefulWidget {
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
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
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SidebarWidget(
|
||||
communities: widget.communities,
|
||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
||||
widget.selectedCommunity?.uuid ??
|
||||
'',
|
||||
communities: communities,
|
||||
selectedSpaceUuid:
|
||||
selectedSpace?.uuid ?? selectedCommunity?.uuid ?? '',
|
||||
),
|
||||
widget.shouldNavigateToSpaceModelPage
|
||||
shouldNavigateToSpaceModelPage
|
||||
? Expanded(
|
||||
child: BlocProvider(
|
||||
create: (context) => SpaceModelBloc(
|
||||
api: SpaceModelManagementApi(),
|
||||
initialSpaceModels: _spaceModels,
|
||||
initialSpaceModels: spaceModels ?? [],
|
||||
),
|
||||
child: SpaceModelPage(
|
||||
products: widget.products,
|
||||
onSpaceModelsUpdated: _onSpaceModelsUpdated,
|
||||
products: products,
|
||||
),
|
||||
),
|
||||
)
|
||||
: CommunityStructureArea(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||
products: widget.products,
|
||||
communities: widget.communities,
|
||||
spaceModels: _spaceModels,
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
spaces: selectedCommunity?.spaces ?? [],
|
||||
products: products,
|
||||
communities: communities,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,6 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
tags[event.index].tag = event.tag;
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
@ -79,7 +78,6 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
errorMessage: _getValidationError(tags),
|
||||
));
|
||||
}
|
||||
});
|
||||
@ -108,13 +106,12 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
emit(AssignTagLoaded(
|
||||
tags: updatedTags,
|
||||
isSaveEnabled: _validateTags(updatedTags),
|
||||
errorMessage: _getValidationError(updatedTags),
|
||||
));
|
||||
} else {
|
||||
emit(const AssignTagLoaded(
|
||||
tags: [],
|
||||
isSaveEnabled: false,
|
||||
errorMessage: 'Failed to delete tag'));
|
||||
tags: [],
|
||||
isSaveEnabled: false,
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -128,10 +125,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
|
||||
String? _getValidationError(List<Tag> tags) {
|
||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
||||
if (hasEmptyTag) {
|
||||
return 'Tags cannot be empty.';
|
||||
}
|
||||
|
||||
if (hasEmptyTag) return 'Tags cannot be empty.';
|
||||
final duplicateTags = tags
|
||||
.map((tag) => tag.tag?.trim() ?? '')
|
||||
.fold<Map<String, int>>({}, (map, tag) {
|
||||
|
@ -21,11 +21,11 @@ class AssignTagLoaded extends AssignTagState {
|
||||
const AssignTagLoaded({
|
||||
required this.tags,
|
||||
required this.isSaveEnabled,
|
||||
required this.errorMessage,
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [tags, isSaveEnabled, errorMessage ?? ''];
|
||||
List<Object> get props => [tags, isSaveEnabled];
|
||||
}
|
||||
|
||||
class AssignTagError extends AssignTagState {
|
||||
|
@ -71,7 +71,6 @@ class AssignTagDialog extends StatelessWidget {
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateProperty.all(
|
||||
ColorsManager.dataHeaderGrey),
|
||||
key: ValueKey(state.tags.length),
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.dataHeaderGrey,
|
||||
width: 1,
|
||||
@ -121,7 +120,6 @@ class AssignTagDialog extends StatelessWidget {
|
||||
final controller = controllers[index];
|
||||
final availableTags = getAvailableTags(
|
||||
allTags ?? [], state.tags, tag);
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(Text((index + 1).toString())),
|
||||
@ -160,8 +158,6 @@ class AssignTagDialog extends StatelessWidget {
|
||||
.add(DeleteTag(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
padding: EdgeInsets.zero,
|
||||
@ -237,13 +233,11 @@ class AssignTagDialog extends StatelessWidget {
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result =
|
||||
TagHelper.processTags(updatedTags, subspaces);
|
||||
final result = processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
final processedSubspaces = result['subspaces'];
|
||||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@ -259,7 +253,6 @@ class AssignTagDialog extends StatelessWidget {
|
||||
spaceTags: processedTags,
|
||||
isCreate: false,
|
||||
onSave: onSave,
|
||||
allTags: allTags,
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -270,21 +263,20 @@ class AssignTagDialog extends StatelessWidget {
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
borderRadius: 10,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: state.isSaveEnabled
|
||||
? ColorsManager.whiteColors
|
||||
: ColorsManager.whiteColorsWithOpacity,
|
||||
backgroundColor: state.isSaveEnabled
|
||||
? ColorsManager.secondaryColor
|
||||
: ColorsManager.grayColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
onPressed: state.isSaveEnabled
|
||||
? () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result = TagHelper.processTags(
|
||||
updatedTags, subspaces);
|
||||
final result =
|
||||
processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
result['subspaces'] as List<SubspaceModel>;
|
||||
onSave?.call(processedTags, processedSubspaces);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
@ -309,12 +301,115 @@ 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;
|
||||
return allTags
|
||||
.where((tagValue) => !currentTags
|
||||
.where((e) => e != currentTag) // Exclude the current row
|
||||
.map((e) => e.tag)
|
||||
.contains(tagValue))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Map<String, dynamic> processTags(
|
||||
List<Tag> updatedTags, List<SubspaceModel>? subspaces) {
|
||||
final modifiedTags = List<Tag>.from(updatedTags);
|
||||
final modifiedSubspaces = List<SubspaceModel>.from(subspaces ?? []);
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
subspace.tags?.removeWhere(
|
||||
(tag) => !modifiedTags
|
||||
.any((updatedTag) => updatedTag.internalId == tag.internalId),
|
||||
);
|
||||
}
|
||||
}
|
||||
for (var tag in modifiedTags.toList()) {
|
||||
if (modifiedSubspaces.isEmpty) continue;
|
||||
|
||||
final prevIndice = checkTagExistInSubspace(tag, modifiedSubspaces);
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
(prevIndice == null ||
|
||||
modifiedSubspaces[prevIndice].subspaceName == 'Main Space')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
prevIndice == null) {
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location != modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location == modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'updatedTags': modifiedTags,
|
||||
'subspaces': modifiedSubspaces,
|
||||
};
|
||||
}
|
||||
|
||||
int? checkTagExistInSubspace(Tag tag, List<SubspaceModel>? 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;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,6 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateProperty.all(
|
||||
ColorsManager.dataHeaderGrey),
|
||||
key: ValueKey(state.tags.length),
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.dataHeaderGrey,
|
||||
width: 1,
|
||||
@ -134,9 +133,8 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
: List.generate(state.tags.length, (index) {
|
||||
final tag = state.tags[index];
|
||||
final controller = controllers[index];
|
||||
final availableTags =
|
||||
TagHelper.getAvailableTagModels(
|
||||
allTags ?? [], state.tags, tag);
|
||||
final availableTags = getAvailableTags(
|
||||
allTags ?? [], state.tags, tag);
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
@ -177,7 +175,6 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
.add(DeleteTagModel(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
padding: EdgeInsets.zero,
|
||||
@ -257,15 +254,11 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
final updatedTags =
|
||||
List<TagModel>.from(state.tags);
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<TagModel>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
final processedSubspaces = result['subspaces'];
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@ -304,25 +297,22 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
borderRadius: 10,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: state.isSaveEnabled
|
||||
? ColorsManager.whiteColors
|
||||
: ColorsManager.whiteColorsWithOpacity,
|
||||
backgroundColor: state.isSaveEnabled
|
||||
? ColorsManager.secondaryColor
|
||||
: ColorsManager.grayColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
onPressed: state.isSaveEnabled
|
||||
? () async {
|
||||
final updatedTags =
|
||||
List<TagModel>.from(state.tags);
|
||||
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<TagModel>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces']
|
||||
as List<dynamic>);
|
||||
result['subspaces']
|
||||
as List<SubspaceTemplateModel>;
|
||||
|
||||
Navigator.of(context)
|
||||
.popUntil((route) => route.isFirst);
|
||||
@ -366,4 +356,120 @@ 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();
|
||||
}
|
||||
|
||||
int? checkTagExistInSubspace(
|
||||
TagModel tag, List<SubspaceTemplateModel>? 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;
|
||||
}
|
||||
|
||||
Map<String, dynamic> processTags(
|
||||
List<TagModel> updatedTags, List<SubspaceTemplateModel>? subspaces) {
|
||||
final modifiedTags = List<TagModel>.from(updatedTags);
|
||||
final modifiedSubspaces = List<SubspaceTemplateModel>.from(subspaces ?? []);
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
subspace.tags?.removeWhere(
|
||||
(tag) => !modifiedTags
|
||||
.any((updatedTag) => updatedTag.internalId == tag.internalId),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (var tag in modifiedTags.toList()) {
|
||||
if (modifiedSubspaces.isEmpty) continue;
|
||||
|
||||
final prevIndice = checkTagExistInSubspace(tag, modifiedSubspaces);
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
(prevIndice == null ||
|
||||
modifiedSubspaces[prevIndice].subspaceName == 'Main Space')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
prevIndice == null) {
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location != modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location == modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'updatedTags': modifiedTags,
|
||||
'subspaces': modifiedSubspaces,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
|
||||
class ConnectionHelper {
|
||||
static Offset getCenterPosition(Size screenSize) {
|
||||
return Offset(
|
||||
screenSize.width / 2 - 260,
|
||||
screenSize.height / 2 - 200,
|
||||
);
|
||||
}
|
||||
|
||||
static bool isHighlightedConnection(
|
||||
Connection connection, SpaceModel? selectedSpace) {
|
||||
if (selectedSpace == null) return true;
|
||||
|
||||
return connection.startSpace == selectedSpace ||
|
||||
connection.endSpace == selectedSpace;
|
||||
}
|
||||
}
|
@ -1,94 +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})";
|
||||
}
|
||||
|
||||
static bool isSave(List<SpaceModel> spaces) {
|
||||
return spaces.isNotEmpty &&
|
||||
spaces.any((space) =>
|
||||
space.status == SpaceStatus.newSpace ||
|
||||
space.status == SpaceStatus.modified ||
|
||||
space.status == SpaceStatus.deleted);
|
||||
}
|
||||
|
||||
static bool isHighlightedSpace(SpaceModel space, SpaceModel? selectedSpace) {
|
||||
if (selectedSpace == null) return true;
|
||||
|
||||
return space == selectedSpace ||
|
||||
selectedSpace.parent?.internalId == space.internalId ||
|
||||
selectedSpace.children
|
||||
?.any((child) => child.internalId == space.internalId) ==
|
||||
true;
|
||||
}
|
||||
|
||||
static bool isNameConflict(
|
||||
String value, SpaceModel? parentSpace, SpaceModel? editSpace) {
|
||||
final siblings = parentSpace?.children
|
||||
.where((child) => child.internalId != editSpace?.internalId)
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
final editSiblings = editSpace?.parent?.children
|
||||
.where((child) => child.internalId != editSpace.internalId)
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
final editSiblingConflict =
|
||||
editSiblings.any((child) => child.name == value);
|
||||
|
||||
final siblingConflict = siblings.any((child) => child.name == value);
|
||||
|
||||
final parentConflict = parentSpace?.name == value &&
|
||||
parentSpace?.internalId != editSpace?.internalId;
|
||||
|
||||
final parentOfEditSpaceConflict = editSpace?.parent?.name == value &&
|
||||
editSpace?.parent?.internalId != editSpace?.internalId;
|
||||
|
||||
final childConflict =
|
||||
editSpace?.children.any((child) => child.name == value) ?? false;
|
||||
|
||||
return siblingConflict ||
|
||||
parentConflict ||
|
||||
editSiblingConflict ||
|
||||
parentOfEditSpaceConflict ||
|
||||
childConflict;
|
||||
}
|
||||
}
|
@ -1,147 +1,12 @@
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_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/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/tag_model.dart';
|
||||
|
||||
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({
|
||||
List<TagModel>? spaceTagModels,
|
||||
List<SubspaceTemplateModel>? subspaces,
|
||||
@ -171,7 +36,7 @@ class TagHelper {
|
||||
return initialTags;
|
||||
}
|
||||
|
||||
static List<Tag> generateInitialForTags({
|
||||
static List<Tag> generateInitialForTags({
|
||||
List<Tag>? spaceTags,
|
||||
List<SubspaceModel>? subspaces,
|
||||
}) {
|
||||
@ -280,85 +145,4 @@ class TagHelper {
|
||||
))
|
||||
.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,
|
||||
);
|
||||
}
|
||||
|
||||
static List<String> getAllTagValues(
|
||||
List<CommunityModel> communities, List<SpaceTemplateModel>? spaceModels) {
|
||||
final Set<String> allTags = {};
|
||||
|
||||
if (spaceModels != null) {
|
||||
for (var model in spaceModels) {
|
||||
allTags.addAll(model.listAllTagValues());
|
||||
}
|
||||
}
|
||||
|
||||
for (final community in communities) {
|
||||
for (final space in community.spaces) {
|
||||
if (space.tags != null) {
|
||||
allTags.addAll(space.listAllTagValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allTags.toList();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
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/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
||||
|
@ -11,10 +11,8 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SpaceModelPage extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
final Function(List<SpaceTemplateModel>)? onSpaceModelsUpdated;
|
||||
|
||||
const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated})
|
||||
: super(key: key);
|
||||
const SpaceModelPage({Key? key, this.products}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +25,6 @@ class SpaceModelPage extends StatelessWidget {
|
||||
final allTagValues = _getAllTagValues(spaceModels);
|
||||
final allSpaceModelNames = _getAllSpaceModelName(spaceModels);
|
||||
|
||||
if (onSpaceModelsUpdated != null) {
|
||||
onSpaceModelsUpdated!(spaceModels);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
body: Padding(
|
||||
|
@ -6,64 +6,55 @@ class ButtonContentWidget extends StatelessWidget {
|
||||
final IconData? icon;
|
||||
final String label;
|
||||
final String? svgAssets;
|
||||
final bool disabled;
|
||||
|
||||
const ButtonContentWidget({
|
||||
Key? key,
|
||||
this.icon,
|
||||
required this.label,
|
||||
this.svgAssets,
|
||||
this.disabled = false,
|
||||
}) : super(key: key);
|
||||
const ButtonContentWidget(
|
||||
{Key? key, this.icon, required this.label, this.svgAssets})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Opacity(
|
||||
opacity: disabled ? 0.5 : 1.0,
|
||||
child: SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 3.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
return SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 3.0,
|
||||
),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (icon != null)
|
||||
Icon(
|
||||
icon,
|
||||
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),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 16,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
child: Row(
|
||||
children: [
|
||||
if (icon != null)
|
||||
Icon(
|
||||
icon,
|
||||
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),
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -129,16 +129,10 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
SubspaceModelCreate(
|
||||
subspaces: state.space.subspaceModels ?? [],
|
||||
tags: state.space.tags ?? [],
|
||||
onSpaceModelUpdate: (updatedSubspaces,updatedTags) {
|
||||
onSpaceModelUpdate: (updatedSubspaces) {
|
||||
context
|
||||
.read<CreateSpaceModelBloc>()
|
||||
.add(AddSubspacesToSpaceTemplate(updatedSubspaces));
|
||||
if(updatedTags!=null){
|
||||
context
|
||||
.read<CreateSpaceModelBloc>()
|
||||
.add(AddTagsToSpaceTemplate(updatedTags));
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
@ -1,7 +1,6 @@
|
||||
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/tag_model.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/space_model/widgets/subspace_name_label_widget.dart';
|
||||
@ -9,16 +8,13 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SubspaceModelCreate extends StatefulWidget {
|
||||
final List<SubspaceTemplateModel> subspaces;
|
||||
final void Function(
|
||||
List<SubspaceTemplateModel> newSubspaces, List<TagModel>? tags)?
|
||||
final void Function(List<SubspaceTemplateModel> newSubspaces)?
|
||||
onSpaceModelUpdate;
|
||||
final List<TagModel> tags;
|
||||
|
||||
const SubspaceModelCreate({
|
||||
Key? key,
|
||||
required this.subspaces,
|
||||
this.onSpaceModelUpdate,
|
||||
required this.tags,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -28,13 +24,11 @@ class SubspaceModelCreate extends StatefulWidget {
|
||||
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
|
||||
@ -111,26 +105,14 @@ class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
||||
isEdit: true,
|
||||
dialogTitle: dialogTitle,
|
||||
existingSubSpaces: _subspaces,
|
||||
|
||||
onUpdate: (subspaceModels) {
|
||||
final updatedIds = subspaceModels.map((s) => s.internalId).toSet();
|
||||
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);
|
||||
errorSubspaceId = null;
|
||||
});
|
||||
if (widget.onSpaceModelUpdate != null) {
|
||||
widget.onSpaceModelUpdate!(subspaceModels, _tags);
|
||||
widget.onSpaceModelUpdate!(subspaceModels);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -52,13 +52,6 @@ class _SubspaceNameDisplayWidgetState extends State<SubspaceNameDisplayWidget> {
|
||||
|
||||
void _handleValidationAndSave() {
|
||||
final updatedName = _controller.text;
|
||||
|
||||
if (updatedName.isEmpty) {
|
||||
setState(() {
|
||||
errorText = 'Subspace name cannot be empty.';
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (widget.validateName(updatedName)) {
|
||||
setState(() {
|
||||
errorText = null;
|
||||
|
@ -18,7 +18,8 @@ class UserPermissionApi {
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
debugPrint('fetchUsers Response: $json');
|
||||
final List<dynamic> data = json['data'] ?? [];
|
||||
final List<dynamic> data =
|
||||
json['data'] ?? []; // Default to an empty list if no data
|
||||
return data.map((item) => RolesUserModel.fromJson(item)).toList();
|
||||
},
|
||||
);
|
||||
@ -118,7 +119,7 @@ class UserPermissionApi {
|
||||
);
|
||||
return response ?? 'Unknown error occurred';
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = e.response?.data['error']['message'];
|
||||
final errorMessage = e.response?.data['error'];
|
||||
return errorMessage;
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
@ -204,6 +205,7 @@ class UserPermissionApi {
|
||||
.replaceAll("{invitedUserUuid}", userUuid),
|
||||
body: bodya,
|
||||
expectedResponseModel: (json) {
|
||||
print('changeUserStatusById==${json['success']}');
|
||||
return json['success'];
|
||||
},
|
||||
);
|
||||
@ -211,6 +213,7 @@ class UserPermissionApi {
|
||||
return response;
|
||||
} catch (e) {
|
||||
return false;
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user