mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Space managment refactoring merge (#239) with dev
<!-- Thanks for contributing! Provide a description of your changes below and a general summary in the title Please look at the following checklist to ensure that your PR can be accepted quickly: --> ## Jira Ticket [SP-0000](https://syncrow.atlassian.net/browse/SP-0000) ## Description merge latest working updates <!--- Describe your changes in detail --> ## Type of Change <!--- Put an `x` in all the boxes that apply: --> - [ ] ✨ New feature (non-breaking change which adds functionality) - [x] 🛠️ Bug fix (non-breaking change which fixes an issue) - [x] ❌ Breaking change (fix or feature that would cause existing functionality to change) - [x] 🧹 Code refactor - [ ] ✅ Build configuration change - [ ] 📝 Documentation - [ ] 🗑️ Chore
This commit is contained in:
@ -17,7 +17,8 @@ class TagDialogTextfieldDropdown extends StatefulWidget {
|
|||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState();
|
_DialogTextfieldDropdownState createState() =>
|
||||||
|
_DialogTextfieldDropdownState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
||||||
@ -36,6 +37,12 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
|
|
||||||
_focusNode.addListener(() {
|
_focusNode.addListener(() {
|
||||||
if (!_focusNode.hasFocus) {
|
if (!_focusNode.hasFocus) {
|
||||||
|
// Call onSelected when focus is lost
|
||||||
|
final selectedTag = _filteredItems.firstWhere(
|
||||||
|
(tag) => tag.tag == _controller.text,
|
||||||
|
orElse: () => Tag(tag: _controller.text),
|
||||||
|
);
|
||||||
|
widget.onSelected(selectedTag);
|
||||||
_closeDropdown();
|
_closeDropdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -43,7 +50,9 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
|
|
||||||
void _filterItems() {
|
void _filterItems() {
|
||||||
setState(() {
|
setState(() {
|
||||||
_filteredItems = widget.items.where((tag) => tag.product?.uuid == widget.product).toList();
|
_filteredItems = widget.items;
|
||||||
|
// .where((tag) => tag.product?.uuid == widget.product)
|
||||||
|
// .toList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +121,9 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyMedium
|
.bodyMedium
|
||||||
?.copyWith(color: ColorsManager.textPrimaryColor)),
|
?.copyWith(
|
||||||
|
color: ColorsManager
|
||||||
|
.textPrimaryColor)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_controller.text = tag.tag ?? '';
|
_controller.text = tag.tag ?? '';
|
||||||
widget.onSelected(tag);
|
widget.onSelected(tag);
|
||||||
@ -156,13 +167,15 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
controller: _controller,
|
controller: _controller,
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
onFieldSubmitted: (value) {
|
onFieldSubmitted: (value) {
|
||||||
final selectedTag = _filteredItems.firstWhere((tag) => tag.tag == value,
|
final selectedTag = _filteredItems.firstWhere(
|
||||||
|
(tag) => tag.tag == value,
|
||||||
orElse: () => Tag(tag: value));
|
orElse: () => Tag(tag: value));
|
||||||
widget.onSelected(selectedTag);
|
widget.onSelected(selectedTag);
|
||||||
_closeDropdown();
|
_closeDropdown();
|
||||||
},
|
},
|
||||||
onTapOutside: (event) {
|
onTapOutside: (event) {
|
||||||
widget.onSelected(_filteredItems.firstWhere((tag) => tag.tag == _controller.text,
|
widget.onSelected(_filteredItems.firstWhere(
|
||||||
|
(tag) => tag.tag == _controller.text,
|
||||||
orElse: () => Tag(tag: _controller.text)));
|
orElse: () => Tag(tag: _controller.text)));
|
||||||
_closeDropdown();
|
_closeDropdown();
|
||||||
},
|
},
|
||||||
|
@ -30,12 +30,13 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
|
|
||||||
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
|
||||||
try {
|
try {
|
||||||
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
var uuid =
|
||||||
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
user = await HomeApi().fetchUserInfo(uuid);
|
user = await HomeApi().fetchUserInfo(uuid);
|
||||||
|
|
||||||
if (user != null && user!.project != null) {
|
if (user != null && user!.project != null) {
|
||||||
await ProjectManager.setProjectUUID(user!.project!.uuid);
|
await ProjectManager.setProjectUUID(user!.project!.uuid);
|
||||||
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
|
// NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
|
||||||
}
|
}
|
||||||
add(FetchTermEvent());
|
add(FetchTermEvent());
|
||||||
add(FetchPolicyEvent());
|
add(FetchPolicyEvent());
|
||||||
@ -67,10 +68,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
Future _confirmUserAgreement(
|
||||||
|
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(LoadingHome());
|
emit(LoadingHome());
|
||||||
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
var uuid =
|
||||||
|
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
|
||||||
policy = await HomeApi().confirmUserAgreements(uuid);
|
policy = await HomeApi().confirmUserAgreements(uuid);
|
||||||
emit(PolicyAgreement());
|
emit(PolicyAgreement());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -123,5 +126,41 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
},
|
},
|
||||||
color: const Color(0xFF023DFE),
|
color: const Color(0xFF023DFE),
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// HomeItemModel(
|
||||||
|
// title: 'Move in',
|
||||||
|
// icon: Assets.moveinIcon,
|
||||||
|
// active: false,
|
||||||
|
// onPress: (context) {},
|
||||||
|
// color: ColorsManager.primaryColor,
|
||||||
|
// ),
|
||||||
|
// HomeItemModel(
|
||||||
|
// title: 'Construction',
|
||||||
|
// icon: Assets.constructionIcon,
|
||||||
|
// active: false,
|
||||||
|
// onPress: (context) {},
|
||||||
|
// color: ColorsManager.primaryColor,
|
||||||
|
// ),
|
||||||
|
// HomeItemModel(
|
||||||
|
// title: 'Energy',
|
||||||
|
// icon: Assets.energyIcon,
|
||||||
|
// active: false,
|
||||||
|
// onPress: (context) {},
|
||||||
|
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||||
|
// ),
|
||||||
|
// HomeItemModel(
|
||||||
|
// title: 'Integrations',
|
||||||
|
// icon: Assets.integrationsIcon,
|
||||||
|
// active: false,
|
||||||
|
// onPress: (context) {},
|
||||||
|
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||||
|
// ),
|
||||||
|
// HomeItemModel(
|
||||||
|
// title: 'Asset',
|
||||||
|
// icon: Assets.assetIcon,
|
||||||
|
// active: false,
|
||||||
|
// onPress: (context) {},
|
||||||
|
// color: ColorsManager.slidingBlueColor.withOpacity(0.2),
|
||||||
|
// ),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
final homeBloc = BlocProvider.of<HomeBloc>(context);
|
||||||
homeBloc.add(const FetchUserInfo());
|
// homeBloc.add(const FetchUserInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -38,8 +38,10 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
child: BlocConsumer<HomeBloc, HomeState>(
|
child: BlocConsumer<HomeBloc, HomeState>(
|
||||||
listener: (BuildContext context, state) {
|
listener: (BuildContext context, state) {
|
||||||
if (state is HomeInitial) {
|
if (state is HomeInitial) {
|
||||||
if (homeBloc.user!.hasAcceptedWebAgreement == false && !_dialogShown) {
|
if (homeBloc.user!.hasAcceptedWebAgreement == false &&
|
||||||
_dialogShown = true; // Set the flag to true to indicate the dialog is showing.
|
!_dialogShown) {
|
||||||
|
_dialogShown =
|
||||||
|
true; // Set the flag to true to indicate the dialog is showing.
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
Future.delayed(const Duration(seconds: 1), () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -54,7 +56,7 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
_dialogShown = false;
|
_dialogShown = false;
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
homeBloc.add(ConfirmUserAgreementEvent());
|
homeBloc.add(ConfirmUserAgreementEvent());
|
||||||
homeBloc.add(const FetchUserInfo());
|
// homeBloc.add(const FetchUserInfo());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -98,7 +100,8 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
width: size.width * 0.68,
|
width: size.width * 0.68,
|
||||||
child: GridView.builder(
|
child: GridView.builder(
|
||||||
itemCount: homeBloc.homeItems.length,
|
itemCount: homeBloc.homeItems.length,
|
||||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: 3, // Adjust as needed.
|
crossAxisCount: 3, // Adjust as needed.
|
||||||
crossAxisSpacing: 20.0,
|
crossAxisSpacing: 20.0,
|
||||||
mainAxisSpacing: 20.0,
|
mainAxisSpacing: 20.0,
|
||||||
@ -111,7 +114,8 @@ class _HomeWebPageState extends State<HomeWebPage> {
|
|||||||
active: homeBloc.homeItems[index].active!,
|
active: homeBloc.homeItems[index].active!,
|
||||||
name: homeBloc.homeItems[index].title!,
|
name: homeBloc.homeItems[index].title!,
|
||||||
img: homeBloc.homeItems[index].icon!,
|
img: homeBloc.homeItems[index].icon!,
|
||||||
onTap: () => homeBloc.homeItems[index].onPress(context),
|
onTap: () =>
|
||||||
|
homeBloc.homeItems[index].onPress(context),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -21,7 +21,8 @@ import 'package:syncrow_web/services/space_mana_api.dart';
|
|||||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||||
import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action;
|
import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action;
|
||||||
|
|
||||||
class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
class SpaceManagementBloc
|
||||||
|
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||||
final CommunitySpaceManagementApi _api;
|
final CommunitySpaceManagementApi _api;
|
||||||
final ProductApi _productApi;
|
final ProductApi _productApi;
|
||||||
final SpaceModelManagementApi _spaceModelApi;
|
final SpaceModelManagementApi _spaceModelApi;
|
||||||
@ -62,7 +63,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
int page = 1;
|
int page = 1;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
final spaceModels = await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||||
|
page: page, projectId: projectUuid);
|
||||||
if (spaceModels.isNotEmpty) {
|
if (spaceModels.isNotEmpty) {
|
||||||
allSpaceModels.addAll(spaceModels);
|
allSpaceModels.addAll(spaceModels);
|
||||||
page++;
|
page++;
|
||||||
@ -75,26 +77,29 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceModelLoaded(
|
emit(SpaceModelLoaded(
|
||||||
communities:
|
communities: state is SpaceManagementLoaded
|
||||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
? (state as SpaceManagementLoaded).communities
|
||||||
|
: [],
|
||||||
products: _cachedProducts ?? [],
|
products: _cachedProducts ?? [],
|
||||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deleteSpaceModelFromCache(
|
void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event,
|
||||||
DeleteSpaceModelFromCache event, Emitter<SpaceManagementState> emit) async {
|
Emitter<SpaceManagementState> emit) async {
|
||||||
if (_cachedSpaceModels != null) {
|
if (_cachedSpaceModels != null) {
|
||||||
_cachedSpaceModels =
|
_cachedSpaceModels = _cachedSpaceModels!
|
||||||
_cachedSpaceModels!.where((model) => model.uuid != event.deletedUuid).toList();
|
.where((model) => model.uuid != event.deletedUuid)
|
||||||
|
.toList();
|
||||||
} else {
|
} else {
|
||||||
_cachedSpaceModels = await fetchSpaceModels();
|
_cachedSpaceModels = await fetchSpaceModels();
|
||||||
}
|
}
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceModelLoaded(
|
emit(SpaceModelLoaded(
|
||||||
communities:
|
communities: state is SpaceManagementLoaded
|
||||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
? (state as SpaceManagementLoaded).communities
|
||||||
|
: [],
|
||||||
products: _cachedProducts ?? [],
|
products: _cachedProducts ?? [],
|
||||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||||
allTags: _cachedTags ?? []));
|
allTags: _cachedTags ?? []));
|
||||||
@ -122,8 +127,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
int page = 1;
|
int page = 1;
|
||||||
|
|
||||||
while (hasNext) {
|
while (hasNext) {
|
||||||
final spaceModels =
|
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||||
await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
page: page, projectId: projectUuid);
|
||||||
if (spaceModels.isNotEmpty) {
|
if (spaceModels.isNotEmpty) {
|
||||||
allSpaceModels.addAll(spaceModels);
|
allSpaceModels.addAll(spaceModels);
|
||||||
page++;
|
page++;
|
||||||
@ -164,10 +169,12 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
await fetchTags();
|
await fetchTags();
|
||||||
|
|
||||||
emit(SpaceManagementLoading());
|
emit(SpaceManagementLoading());
|
||||||
final success = await _api.updateCommunity(event.communityUuid, event.name, projectUuid);
|
final success = await _api.updateCommunity(
|
||||||
|
event.communityUuid, event.name, projectUuid);
|
||||||
if (success) {
|
if (success) {
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities);
|
final updatedCommunities =
|
||||||
|
List<CommunityModel>.from(previousState.communities);
|
||||||
for (var community in updatedCommunities) {
|
for (var community in updatedCommunities) {
|
||||||
if (community.uuid == event.communityUuid) {
|
if (community.uuid == event.communityUuid) {
|
||||||
community.name = event.name;
|
community.name = event.name;
|
||||||
@ -212,7 +219,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(String communityUuid) async {
|
Future<List<SpaceModel>> _fetchSpacesForCommunity(
|
||||||
|
String communityUuid) async {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
||||||
@ -242,20 +250,23 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onBlankState(BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
Future<void> _onBlankState(
|
||||||
|
BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||||
try {
|
try {
|
||||||
final previousState = state;
|
final previousState = state;
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||||
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
var spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
List<CommunityModel> communities =
|
||||||
|
await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
await fetchSpaceModels();
|
await fetchSpaceModels();
|
||||||
await fetchTags();
|
// await fetchTags();
|
||||||
|
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
if (previousState is SpaceManagementLoaded ||
|
||||||
|
previousState is BlankState) {
|
||||||
final prevCommunities = (previousState as dynamic).communities ?? [];
|
final prevCommunities = (previousState as dynamic).communities ?? [];
|
||||||
emit(BlankState(
|
emit(BlankState(
|
||||||
communities: List<CommunityModel>.from(prevCommunities),
|
communities: List<CommunityModel>.from(prevCommunities),
|
||||||
@ -286,7 +297,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
_onloadProducts();
|
_onloadProducts();
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
// Wait until `communityList` is loaded
|
// Wait until `communityList` is loaded
|
||||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc, spaceTreeState);
|
List<CommunityModel> communities =
|
||||||
|
await _waitForCommunityList(spaceBloc, spaceTreeState);
|
||||||
|
|
||||||
// Fetch space models after communities are available
|
// Fetch space models after communities are available
|
||||||
final prevSpaceModels = await fetchSpaceModels();
|
final prevSpaceModels = await fetchSpaceModels();
|
||||||
@ -310,8 +322,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
final completer = Completer<List<CommunityModel>>();
|
final completer = Completer<List<CommunityModel>>();
|
||||||
final subscription = spaceBloc.stream.listen((state) {
|
final subscription = spaceBloc.stream.listen((state) {
|
||||||
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
if (!completer.isCompleted && state.communityList.isNotEmpty) {
|
||||||
completer
|
completer.complete(state.searchQuery.isNotEmpty
|
||||||
.complete(state.searchQuery.isNotEmpty ? state.filteredCommunity : state.communityList);
|
? state.filteredCommunity
|
||||||
|
: state.communityList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
@ -339,7 +352,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
emit(SpaceManagementLoading());
|
emit(SpaceManagementLoading());
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
|
|
||||||
final success = await _api.deleteCommunity(event.communityUuid, projectUuid);
|
final success =
|
||||||
|
await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||||
if (success) {
|
if (success) {
|
||||||
// add(LoadCommunityAndSpacesEvent());
|
// add(LoadCommunityAndSpacesEvent());
|
||||||
} else {
|
} else {
|
||||||
@ -361,12 +375,13 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
await fetchTags();
|
await fetchTags();
|
||||||
CommunityModel? newCommunity =
|
CommunityModel? newCommunity = await _api.createCommunity(
|
||||||
await _api.createCommunity(event.name, event.description, projectUuid);
|
event.name, event.description, projectUuid);
|
||||||
var prevSpaceModels = await fetchSpaceModels();
|
var prevSpaceModels = await fetchSpaceModels();
|
||||||
|
|
||||||
if (newCommunity != null) {
|
if (newCommunity != null) {
|
||||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
if (previousState is SpaceManagementLoaded ||
|
||||||
|
previousState is BlankState) {
|
||||||
final prevCommunities = List<CommunityModel>.from(
|
final prevCommunities = List<CommunityModel>.from(
|
||||||
(previousState as dynamic).communities,
|
(previousState as dynamic).communities,
|
||||||
);
|
);
|
||||||
@ -459,12 +474,15 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
try {
|
try {
|
||||||
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
final spaceTreeState = event.context.read<SpaceTreeBloc>().state;
|
||||||
|
|
||||||
final updatedSpaces =
|
final updatedSpaces = await saveSpacesHierarchically(
|
||||||
await saveSpacesHierarchically(event.context, event.spaces, event.communityUuid);
|
event.context, event.spaces, event.communityUuid);
|
||||||
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
||||||
|
|
||||||
emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
// emit(SpaceCreationSuccess(spaces: updatedSpaces));
|
||||||
|
// updatedSpaces.forEach(
|
||||||
|
// (element) => element.uuid,
|
||||||
|
// );
|
||||||
|
// final lastUpdatedSpaced = updatedSpaces..addAll(allSpaces);
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
await _updateLoadedState(
|
await _updateLoadedState(
|
||||||
spaceTreeState,
|
spaceTreeState,
|
||||||
@ -475,7 +493,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(SpaceManagementError('Error saving spaces: $e'));
|
// emit(SpaceManagementError('Error saving spaces: $e'));
|
||||||
|
|
||||||
if (previousState is SpaceManagementLoaded) {
|
if (previousState is SpaceManagementLoaded) {
|
||||||
emit(previousState);
|
emit(previousState);
|
||||||
@ -515,13 +533,15 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emit(previousState);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
rethrow;
|
emit(previousState);
|
||||||
|
// rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<SpaceModel>> saveSpacesHierarchically(
|
Future<List<SpaceModel>> saveSpacesHierarchically(BuildContext context,
|
||||||
BuildContext context, List<SpaceModel> spaces, String communityUuid) async {
|
List<SpaceModel> spaces, String communityUuid) async {
|
||||||
final orderedSpaces = flattenHierarchy(spaces);
|
final orderedSpaces = flattenHierarchy(spaces);
|
||||||
|
|
||||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||||
@ -534,6 +554,14 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
selectedCommunity = filteredCommunities.firstWhere(
|
selectedCommunity = filteredCommunities.firstWhere(
|
||||||
(community) => community.uuid == communityUuid,
|
(community) => community.uuid == communityUuid,
|
||||||
|
orElse: () => CommunityModel(
|
||||||
|
uuid: '',
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
updatedAt: DateTime.now(),
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
spaces: spaces,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return [];
|
return [];
|
||||||
@ -548,9 +576,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
if (parent.uuid != null) {
|
if (parent.uuid != null) {
|
||||||
await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid);
|
await _api.deleteSpace(communityUuid, parent.uuid!, projectUuid);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
orderedSpaces.removeWhere((space) => parentsToDelete.contains(space));
|
orderedSpaces.removeWhere((space) => parentsToDelete.contains(space));
|
||||||
|
|
||||||
@ -564,7 +590,7 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
|
|
||||||
if (matchedSpaces.isEmpty) continue;
|
if (matchedSpaces.isEmpty) continue;
|
||||||
|
|
||||||
final prevSpace = matchedSpaces[0];
|
final prevSpace = matchedSpaces.elementAtOrNull(0);
|
||||||
|
|
||||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||||
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
||||||
@ -575,17 +601,19 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
if (prevSubspaces != null || newSubspaces != null) {
|
if (prevSubspaces != null || newSubspaces != null) {
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
if (prevSubspaces != null && newSubspaces != null) {
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
final existsInNew =
|
final existsInNew = newSubspaces
|
||||||
newSubspaces.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
action: custom_action.Action.delete, uuid: prevSubspace.uuid));
|
action: custom_action.Action.delete,
|
||||||
|
uuid: prevSubspace.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (prevSubspaces != null && newSubspaces == null) {
|
} else if (prevSubspaces != null && newSubspaces == null) {
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
action: custom_action.Action.delete, uuid: prevSubspace.uuid));
|
action: custom_action.Action.delete,
|
||||||
|
uuid: prevSubspace.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +641,9 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
if (prevSubspaces != null && newSubspaces != null) {
|
||||||
final newSubspaceMap = {for (var subspace in newSubspaces) subspace.uuid: subspace};
|
final newSubspaceMap = {
|
||||||
|
for (var subspace in newSubspaces) subspace.uuid: subspace
|
||||||
|
};
|
||||||
|
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||||
@ -639,9 +669,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
subspaces: subspaceUpdates,
|
subspaces: space.subspaces,
|
||||||
tags: tagUpdates,
|
// subspaceUpdates,
|
||||||
direction: space.incomingConnection?.direction,
|
tags: space.tags,
|
||||||
|
// tagUpdates,
|
||||||
spaceModelUuid: space.spaceModel?.uuid,
|
spaceModelUuid: space.spaceModel?.uuid,
|
||||||
projectId: projectUuid);
|
projectId: projectUuid);
|
||||||
} else {
|
} else {
|
||||||
@ -651,8 +682,10 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
var createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||||
final tagBodyModels =
|
final tagBodyModels = subspace.tags
|
||||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
?.map((tag) => tag.toCreateTagBodyModel())
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
return CreateSubspaceModel()
|
return CreateSubspaceModel()
|
||||||
..subspaceName = subspace.subspaceName
|
..subspaceName = subspace.subspaceName
|
||||||
..tags = tagBodyModels;
|
..tags = tagBodyModels;
|
||||||
@ -671,7 +704,6 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
isPrivate: space.isPrivate,
|
isPrivate: space.isPrivate,
|
||||||
position: space.position,
|
position: space.position,
|
||||||
icon: space.icon,
|
icon: space.icon,
|
||||||
direction: space.incomingConnection?.direction,
|
|
||||||
spaceModelUuid: space.spaceModel?.uuid,
|
spaceModelUuid: space.spaceModel?.uuid,
|
||||||
tags: tagBodyModels,
|
tags: tagBodyModels,
|
||||||
subspaces: createSubspaceBodyModels,
|
subspaces: createSubspaceBodyModels,
|
||||||
@ -679,7 +711,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
space.uuid = response?.uuid;
|
space.uuid = response?.uuid;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rethrow; // Stop further execution on failure
|
return [];
|
||||||
|
// Stop further execution on failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return spaces;
|
return spaces;
|
||||||
@ -710,7 +743,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return result.toList(); // Convert back to a list
|
return result.toList(); // Convert back to a list
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onLoadSpaceModel(SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
void _onLoadSpaceModel(
|
||||||
|
SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
||||||
emit(SpaceManagementLoading());
|
emit(SpaceManagementLoading());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -757,14 +791,17 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
// Case 1: Tags deleted
|
// Case 1: Tags deleted
|
||||||
if (prevTags != null && newTags != null) {
|
if (prevTags != null && newTags != null) {
|
||||||
for (var prevTag in prevTags) {
|
for (var prevTag in prevTags) {
|
||||||
final existsInNew = newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
final existsInNew =
|
||||||
|
newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid));
|
tagUpdates.add(TagModelUpdate(
|
||||||
|
action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (prevTags != null && newTags == null) {
|
} else if (prevTags != null && newTags == null) {
|
||||||
for (var prevTag in prevTags) {
|
for (var prevTag in prevTags) {
|
||||||
tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid));
|
tagUpdates.add(TagModelUpdate(
|
||||||
|
action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,15 +844,16 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
|
|||||||
return tagUpdates;
|
return tagUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<SpaceModel> findMatchingSpaces(List<SpaceModel> spaces, String targetUuid) {
|
List<SpaceModel> findMatchingSpaces(
|
||||||
|
List<SpaceModel> spaces, String targetUuid) {
|
||||||
List<SpaceModel> matched = [];
|
List<SpaceModel> matched = [];
|
||||||
|
|
||||||
for (var space in spaces) {
|
for (var space in spaces) {
|
||||||
if (space.uuid == targetUuid) {
|
if (space.uuid == targetUuid) {
|
||||||
matched.add(space);
|
matched.add(space);
|
||||||
}
|
}
|
||||||
matched
|
matched.addAll(findMatchingSpaces(
|
||||||
.addAll(findMatchingSpaces(space.children, targetUuid)); // Recursively search in children
|
space.children, targetUuid)); // Recursively search in children
|
||||||
}
|
}
|
||||||
|
|
||||||
return matched;
|
return matched;
|
||||||
|
@ -3,23 +3,26 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
|
|||||||
class Connection {
|
class Connection {
|
||||||
final SpaceModel startSpace;
|
final SpaceModel startSpace;
|
||||||
final SpaceModel endSpace;
|
final SpaceModel endSpace;
|
||||||
final String direction;
|
|
||||||
|
|
||||||
Connection({required this.startSpace, required this.endSpace, required this.direction});
|
Connection({
|
||||||
|
required this.startSpace,
|
||||||
|
required this.endSpace,
|
||||||
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
'startUuid': startSpace.uuid ?? 'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces
|
'startUuid': startSpace.uuid ??
|
||||||
'endUuid': endSpace.uuid ?? 'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces
|
'unsaved-start-space-${startSpace.name}', // Fallback for unsaved spaces
|
||||||
'direction': direction,
|
'endUuid': endSpace.uuid ??
|
||||||
|
'unsaved-end-space-${endSpace.name}', // Fallback for unsaved spaces
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Connection fromMap(Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
static Connection fromMap(
|
||||||
|
Map<String, dynamic> map, Map<String, SpaceModel> spaces) {
|
||||||
return Connection(
|
return Connection(
|
||||||
startSpace: spaces[map['startUuid']]!,
|
startSpace: spaces[map['startUuid']]!,
|
||||||
endSpace: spaces[map['endUuid']]!,
|
endSpace: spaces[map['endUuid']]!,
|
||||||
direction: map['direction'],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
|
import 'selected_product_model.dart';
|
||||||
|
|
||||||
class ProductModel {
|
class ProductModel {
|
||||||
final String uuid;
|
final String uuid;
|
||||||
final String catName;
|
final String catName;
|
||||||
@ -38,6 +40,15 @@ class ProductModel {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectedProduct toSelectedProduct(int count) {
|
||||||
|
return SelectedProduct(
|
||||||
|
productId: uuid,
|
||||||
|
count: count,
|
||||||
|
productName: name!,
|
||||||
|
product: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static String _mapIconToProduct(String prodType) {
|
static String _mapIconToProduct(String prodType) {
|
||||||
const iconMapping = {
|
const iconMapping = {
|
||||||
'1G': Assets.Gang1SwitchIcon,
|
'1G': Assets.Gang1SwitchIcon,
|
||||||
|
@ -101,7 +101,7 @@ class SpaceModel {
|
|||||||
spaceModel: json['spaceModel'] != null
|
spaceModel: json['spaceModel'] != null
|
||||||
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
? SpaceTemplateModel.fromJson(json['spaceModel'])
|
||||||
: null,
|
: null,
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['productAllocations'] as List<dynamic>?)
|
||||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
?.where((item) => item is Map<String, dynamic>) // Validate type
|
||||||
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
@ -116,7 +116,6 @@ class SpaceModel {
|
|||||||
instance.incomingConnection = Connection(
|
instance.incomingConnection = Connection(
|
||||||
startSpace: instance.parent ?? instance, // Parent space
|
startSpace: instance.parent ?? instance, // Parent space
|
||||||
endSpace: instance, // This space instance
|
endSpace: instance, // This space instance
|
||||||
direction: conn['direction'],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class SubspaceModel {
|
|||||||
subspaceName: json['subspaceName'] ?? '',
|
subspaceName: json['subspaceName'] ?? '',
|
||||||
disabled: json['disabled'] ?? false,
|
disabled: json['disabled'] ?? false,
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
tags: (json['tags'] as List<dynamic>?)
|
tags: (json['productAllocations'] as List<dynamic>?)
|
||||||
?.map((item) => Tag.fromJson(item))
|
?.map((item) => Tag.fromJson(item))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
@ -36,7 +36,7 @@ class SubspaceModel {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'uuid': uuid,
|
if (uuid != null) 'uuid': uuid,
|
||||||
'subspaceName': subspaceName,
|
'subspaceName': subspaceName,
|
||||||
'disabled': disabled,
|
'disabled': disabled,
|
||||||
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
'tags': tags?.map((e) => e.toJson()).toList() ?? [],
|
||||||
|
@ -23,10 +23,13 @@ class Tag extends BaseTag {
|
|||||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||||
|
|
||||||
return Tag(
|
return Tag(
|
||||||
uuid: json['uuid'] ?? '',
|
//TODO:insure UUId for tag or prodAlloc
|
||||||
|
uuid: json['name'] != null ? json['uuid'] : json['tag']?['uuid'] ?? '',
|
||||||
internalId: internalId,
|
internalId: internalId,
|
||||||
tag: json['name'] ?? '',
|
tag: json['name'] ?? json['tag']?['name'] ?? '',
|
||||||
product: json['product'] != null ? ProductModel.fromMap(json['product']) : null,
|
product: json['product'] != null
|
||||||
|
? ProductModel.fromMap(json['product'])
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +52,10 @@ class Tag extends BaseTag {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return {
|
return {
|
||||||
'uuid': uuid,
|
if (uuid != null) 'uuid': uuid,
|
||||||
'tag': tag,
|
'name': tag,
|
||||||
'product': product?.toMap(),
|
'productUuid': product?.uuid,
|
||||||
|
// .toMap(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
_nameController = TextEditingController(
|
_nameController = TextEditingController(
|
||||||
text: widget.selectedCommunity?.name ?? '',
|
text: widget.selectedCommunity?.name ?? '',
|
||||||
@ -96,13 +97,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
if (oldWidget.spaces != widget.spaces) {
|
if (oldWidget.spaces != widget.spaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
realignTree();
|
realignTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||||
|
widget.selectedSpace != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_moveToSpace(widget.selectedSpace!);
|
_moveToSpace(widget.selectedSpace!);
|
||||||
});
|
});
|
||||||
@ -185,7 +188,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
connection, widget.selectedSpace)
|
connection, widget.selectedSpace)
|
||||||
? 1.0
|
? 1.0
|
||||||
: 0.3, // Adjust opacity
|
: 0.3, // Adjust opacity
|
||||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
child: CustomPaint(
|
||||||
|
painter: CurvedLinePainter([connection])),
|
||||||
),
|
),
|
||||||
for (var entry in spaces.asMap().entries)
|
for (var entry in spaces.asMap().entries)
|
||||||
if (entry.value.status != SpaceStatus.deleted &&
|
if (entry.value.status != SpaceStatus.deleted &&
|
||||||
@ -195,11 +199,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
top: entry.value.position.dy,
|
top: entry.value.position.dy,
|
||||||
child: SpaceCardWidget(
|
child: SpaceCardWidget(
|
||||||
index: entry.key,
|
index: entry.key,
|
||||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
onButtonTap: (int index, Offset newPosition) {
|
||||||
_showCreateSpaceDialog(screenSize,
|
_showCreateSpaceDialog(screenSize,
|
||||||
position: spaces[index].position + newPosition,
|
position:
|
||||||
|
spaces[index].position + newPosition,
|
||||||
parentIndex: index,
|
parentIndex: index,
|
||||||
direction: direction,
|
|
||||||
projectTags: widget.projectTags);
|
projectTags: widget.projectTags);
|
||||||
},
|
},
|
||||||
position: entry.value.position,
|
position: entry.value.position,
|
||||||
@ -210,7 +214,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
_updateNodePosition(entry.value, newPosition);
|
_updateNodePosition(entry.value, newPosition);
|
||||||
},
|
},
|
||||||
buildSpaceContainer: (int index) {
|
buildSpaceContainer: (int index) {
|
||||||
final bool isHighlighted = SpaceHelper.isHighlightedSpace(
|
final bool isHighlighted =
|
||||||
|
SpaceHelper.isHighlightedSpace(
|
||||||
spaces[index], widget.selectedSpace);
|
spaces[index], widget.selectedSpace);
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
@ -289,7 +294,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
void _showCreateSpaceDialog(Size screenSize,
|
void _showCreateSpaceDialog(Size screenSize,
|
||||||
{Offset? position,
|
{Offset? position,
|
||||||
int? parentIndex,
|
int? parentIndex,
|
||||||
String? direction,
|
|
||||||
double? canvasWidth,
|
double? canvasWidth,
|
||||||
double? canvasHeight,
|
double? canvasHeight,
|
||||||
required List<Tag> projectTags}) {
|
required List<Tag> projectTags}) {
|
||||||
@ -299,19 +303,25 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return CreateSpaceDialog(
|
return CreateSpaceDialog(
|
||||||
products: widget.products,
|
products: widget.products,
|
||||||
spaceModels: widget.spaceModels,
|
spaceModels: widget.spaceModels,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags:
|
||||||
|
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
onCreateSpace: (String name,
|
||||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
String icon,
|
||||||
|
List<SelectedProduct> selectedProducts,
|
||||||
|
SpaceTemplateModel? spaceModel,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Set the first space in the center or use passed position
|
// Set the first space in the center or use passed position
|
||||||
Offset newPosition;
|
Offset newPosition;
|
||||||
if (parentIndex != null) {
|
if (parentIndex != null) {
|
||||||
newPosition =
|
newPosition = getBalancedChildPosition(
|
||||||
getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position
|
spaces[parentIndex]); // Ensure balanced position
|
||||||
} else {
|
} else {
|
||||||
newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
newPosition =
|
||||||
|
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel newSpace = SpaceModel(
|
SpaceModel newSpace = SpaceModel(
|
||||||
@ -325,14 +335,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
tags: tags);
|
tags: tags);
|
||||||
|
|
||||||
if (parentIndex != null && direction != null) {
|
if (parentIndex != null) {
|
||||||
SpaceModel parentSpace = spaces[parentIndex];
|
SpaceModel parentSpace = spaces[parentIndex];
|
||||||
parentSpace.internalId = spaces[parentIndex].internalId;
|
parentSpace.internalId = spaces[parentIndex].internalId;
|
||||||
newSpace.parent = parentSpace;
|
newSpace.parent = parentSpace;
|
||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: parentSpace,
|
startSpace: parentSpace,
|
||||||
endSpace: newSpace,
|
endSpace: newSpace,
|
||||||
direction: direction,
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
newSpace.incomingConnection = newConnection;
|
newSpace.incomingConnection = newConnection;
|
||||||
@ -360,16 +369,21 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
name: widget.selectedSpace!.name,
|
name: widget.selectedSpace!.name,
|
||||||
icon: widget.selectedSpace!.icon,
|
icon: widget.selectedSpace!.icon,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
parentSpace:
|
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||||
SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces),
|
widget.selectedSpace?.parent?.internalId, spaces),
|
||||||
editSpace: widget.selectedSpace,
|
editSpace: widget.selectedSpace,
|
||||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||||
tags: widget.selectedSpace?.tags,
|
tags: widget.selectedSpace?.tags,
|
||||||
subspaces: widget.selectedSpace?.subspaces,
|
subspaces: widget.selectedSpace?.subspaces,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags: TagHelper.getAllTagValues(
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
widget.communities, widget.spaceModels),
|
||||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
onCreateSpace: (String name,
|
||||||
|
String icon,
|
||||||
|
List<SelectedProduct> selectedProducts,
|
||||||
|
SpaceTemplateModel? spaceModel,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Update the space's properties
|
// Update the space's properties
|
||||||
widget.selectedSpace!.name = name;
|
widget.selectedSpace!.name = name;
|
||||||
@ -379,7 +393,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
widget.selectedSpace!.tags = tags;
|
widget.selectedSpace!.tags = tags;
|
||||||
|
|
||||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
||||||
widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified
|
widget.selectedSpace!.status =
|
||||||
|
SpaceStatus.modified; // Mark as modified
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var space in spaces) {
|
for (var space in spaces) {
|
||||||
@ -410,7 +425,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
Map<String, SpaceModel> idToSpace = {};
|
Map<String, SpaceModel> idToSpace = {};
|
||||||
|
|
||||||
void flatten(SpaceModel space) {
|
void flatten(SpaceModel space) {
|
||||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
if (space.status == SpaceStatus.deleted ||
|
||||||
|
space.status == SpaceStatus.parentDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.add(space);
|
result.add(space);
|
||||||
@ -447,7 +463,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
Connection(
|
Connection(
|
||||||
startSpace: parent,
|
startSpace: parent,
|
||||||
endSpace: child,
|
endSpace: child,
|
||||||
direction: "down",
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -532,13 +547,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity,
|
||||||
|
selectedSpace: space),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deselectSpace(BuildContext context) {
|
void _deselectSpace(BuildContext context) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +726,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
||||||
|
|
||||||
duplicated.position = Offset(space.position.dx + 300, space.position.dy + 100);
|
duplicated.position =
|
||||||
|
Offset(space.position.dx + 300, space.position.dy + 100);
|
||||||
List<SpaceModel> duplicatedSubtree = [];
|
List<SpaceModel> duplicatedSubtree = [];
|
||||||
void collectSubtree(SpaceModel node) {
|
void collectSubtree(SpaceModel node) {
|
||||||
duplicatedSubtree.add(node);
|
duplicatedSubtree.add(node);
|
||||||
@ -726,7 +745,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: parent,
|
startSpace: parent,
|
||||||
endSpace: duplicated,
|
endSpace: duplicated,
|
||||||
direction: "down",
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
duplicated.incomingConnection = newConnection;
|
duplicated.incomingConnection = newConnection;
|
||||||
@ -739,7 +757,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
final duplicatedName =
|
||||||
|
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
|
|
||||||
final newSpace = SpaceModel(
|
final newSpace = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
@ -761,7 +780,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
final newConnection = Connection(
|
final newConnection = Connection(
|
||||||
startSpace: newSpace,
|
startSpace: newSpace,
|
||||||
endSpace: duplicatedChild,
|
endSpace: duplicatedChild,
|
||||||
direction: "down",
|
|
||||||
);
|
);
|
||||||
connections.add(newConnection);
|
connections.add(newConnection);
|
||||||
|
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
import '../../../../../common/edit_chip.dart';
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
import '../../model/subspace_model.dart';
|
||||||
|
import '../../model/tag.dart';
|
||||||
|
|
||||||
|
class DevicesPartWidget extends StatelessWidget {
|
||||||
|
const DevicesPartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.tags,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.onEditChip,
|
||||||
|
required this.onTextButtonPressed,
|
||||||
|
required this.isTagsAndSubspaceModelDisabled,
|
||||||
|
});
|
||||||
|
final bool isTagsAndSubspaceModelDisabled;
|
||||||
|
final void Function() onEditChip;
|
||||||
|
final void Function() onTextButtonPressed;
|
||||||
|
final double screenWidth;
|
||||||
|
final List<Tag>? tags;
|
||||||
|
final List<SubspaceModel>? subspaces;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
(tags?.isNotEmpty == true ||
|
||||||
|
subspaces?.any(
|
||||||
|
(subspace) => subspace.tags?.isNotEmpty == true) ==
|
||||||
|
true)
|
||||||
|
? SizedBox(
|
||||||
|
width: screenWidth * 0.25,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
width: 3.0, // Border width
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
// Combine tags from spaceModel and subspaces
|
||||||
|
...TagHelper.groupTags([
|
||||||
|
...?tags,
|
||||||
|
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
||||||
|
]).entries.map(
|
||||||
|
(entry) => Chip(
|
||||||
|
avatar: SizedBox(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
entry.key.icon ?? 'assets/icons/gateway.svg',
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
label: Text(
|
||||||
|
'x${entry.value}', // Show count
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.spaceColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
EditChip(onTap: onEditChip)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: TextButton(
|
||||||
|
onPressed: onTextButtonPressed,
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
icon: Icons.add,
|
||||||
|
label: 'Add Devices',
|
||||||
|
disabled: isTagsAndSubspaceModelDisabled,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../../utils/constants/assets.dart';
|
||||||
|
|
||||||
|
class IconChoosePartWidget extends StatelessWidget {
|
||||||
|
const IconChoosePartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.selectedIcon,
|
||||||
|
required this.showIconSelection,
|
||||||
|
required this.screenWidth,
|
||||||
|
});
|
||||||
|
final double screenWidth;
|
||||||
|
final String selectedIcon;
|
||||||
|
final void Function() showIconSelection;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 50),
|
||||||
|
Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: screenWidth * 0.1,
|
||||||
|
height: screenWidth * 0.1,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SvgPicture.asset(
|
||||||
|
selectedIcon,
|
||||||
|
width: screenWidth * 0.04,
|
||||||
|
height: screenWidth * 0.04,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 20,
|
||||||
|
right: 20,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: showIconSelection,
|
||||||
|
child: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.iconEdit,
|
||||||
|
width: 16,
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../../utils/constants/assets.dart';
|
||||||
|
import '../../../space_model/models/space_template_model.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
|
||||||
|
class SpaceModelLinkingWidget extends StatelessWidget {
|
||||||
|
const SpaceModelLinkingWidget({
|
||||||
|
super.key,
|
||||||
|
required this.onDeleted,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.selectedSpaceModel,
|
||||||
|
required this.isSpaceModelDisabled,
|
||||||
|
});
|
||||||
|
final bool isSpaceModelDisabled;
|
||||||
|
final void Function()? onDeleted;
|
||||||
|
final void Function()? onPressed;
|
||||||
|
final double screenWidth;
|
||||||
|
final SpaceTemplateModel? selectedSpaceModel;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
selectedSpaceModel == null
|
||||||
|
? TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
svgAssets: Assets.link,
|
||||||
|
label: 'Link a space model',
|
||||||
|
disabled: isSpaceModelDisabled,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(
|
||||||
|
width: screenWidth * 0.25,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10.0, horizontal: 16.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
Chip(
|
||||||
|
label: Text(
|
||||||
|
selectedSpaceModel?.modelName ?? '',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(color: ColorsManager.spaceColor),
|
||||||
|
),
|
||||||
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: ColorsManager.transparentColor,
|
||||||
|
width: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
deleteIcon: Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 16,
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDeleted: onDeleted),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
|
||||||
|
class SpaceNameTextfieldWidget extends StatelessWidget {
|
||||||
|
SpaceNameTextfieldWidget({
|
||||||
|
super.key,
|
||||||
|
required this.isNameFieldExist,
|
||||||
|
required this.isNameFieldInvalid,
|
||||||
|
required this.onChange,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.nameController,
|
||||||
|
});
|
||||||
|
TextEditingController nameController;
|
||||||
|
final void Function(String value) onChange;
|
||||||
|
final double screenWidth;
|
||||||
|
bool isNameFieldExist;
|
||||||
|
bool isNameFieldInvalid;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: screenWidth * 0.25,
|
||||||
|
child: TextField(
|
||||||
|
controller: nameController,
|
||||||
|
onChanged: onChange,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Please enter the name',
|
||||||
|
hintStyle: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(color: ColorsManager.lightGrayColor),
|
||||||
|
filled: true,
|
||||||
|
fillColor: ColorsManager.boxColor,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: isNameFieldInvalid || isNameFieldExist
|
||||||
|
? ColorsManager.red
|
||||||
|
: ColorsManager.boxColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
borderSide: const BorderSide(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isNameFieldInvalid)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'*Space name should not be empty.',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isNameFieldExist)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
'*Name already exist',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: ColorsManager.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../../../../../common/edit_chip.dart';
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../space_model/widgets/button_content_widget.dart';
|
||||||
|
import '../../../space_model/widgets/subspace_name_label_widget.dart';
|
||||||
|
import '../../model/subspace_model.dart';
|
||||||
|
|
||||||
|
class SubSpacePartWidget extends StatefulWidget {
|
||||||
|
SubSpacePartWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.onPressed,
|
||||||
|
required this.isTagsAndSubspaceModelDisabled,
|
||||||
|
required this.screenWidth,
|
||||||
|
required this.editChipOnTap,
|
||||||
|
});
|
||||||
|
double screenWidth;
|
||||||
|
bool isTagsAndSubspaceModelDisabled;
|
||||||
|
final void Function() editChipOnTap;
|
||||||
|
List<SubspaceModel>? subspaces;
|
||||||
|
final void Function() onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SubSpacePartWidget> createState() => _SubSpacePartWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SubSpacePartWidgetState extends State<SubSpacePartWidget> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
widget.subspaces == null || widget.subspaces!.isEmpty
|
||||||
|
? TextButton(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
overlayColor: ColorsManager.transparentColor,
|
||||||
|
),
|
||||||
|
onPressed: widget.onPressed,
|
||||||
|
child: ButtonContentWidget(
|
||||||
|
icon: Icons.add,
|
||||||
|
label: 'Create Sub Spaces',
|
||||||
|
disabled: widget.isTagsAndSubspaceModelDisabled,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: widget.screenWidth * 0.25,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.textFieldGreyColor,
|
||||||
|
width: 3.0, // Border width
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8.0,
|
||||||
|
runSpacing: 8.0,
|
||||||
|
children: [
|
||||||
|
if (widget.subspaces != null)
|
||||||
|
...widget.subspaces!.map((subspace) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
SubspaceNameDisplayWidget(
|
||||||
|
text: subspace.subspaceName,
|
||||||
|
validateName: (updatedName) {
|
||||||
|
bool nameExists = widget.subspaces!.any((s) {
|
||||||
|
bool isSameId =
|
||||||
|
s.internalId == subspace.internalId;
|
||||||
|
bool isSameName =
|
||||||
|
s.subspaceName.trim().toLowerCase() ==
|
||||||
|
updatedName.trim().toLowerCase();
|
||||||
|
|
||||||
|
return !isSameId && isSameName;
|
||||||
|
});
|
||||||
|
|
||||||
|
return !nameExists;
|
||||||
|
},
|
||||||
|
onNameChanged: (updatedName) {
|
||||||
|
setState(() {
|
||||||
|
subspace.subspaceName = updatedName;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
EditChip(
|
||||||
|
onTap: widget.editChipOnTap,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -30,27 +30,12 @@ class CurvedLinePainter extends CustomPainter {
|
|||||||
Offset end = connection.endSpace.position +
|
Offset end = connection.endSpace.position +
|
||||||
const Offset(75, 0); // Center top of end space
|
const Offset(75, 0); // Center top of end space
|
||||||
|
|
||||||
if (connection.direction == 'down') {
|
|
||||||
// Curved line for down connections
|
// Curved line for down connections
|
||||||
final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50);
|
final controlPoint = Offset((start.dx + end.dx) / 2, start.dy + 50);
|
||||||
final path = Path()
|
final path = Path()
|
||||||
..moveTo(start.dx, start.dy)
|
..moveTo(start.dx, start.dy)
|
||||||
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
|
..quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
|
||||||
canvas.drawPath(path, paint);
|
canvas.drawPath(path, paint);
|
||||||
} else if (connection.direction == 'right') {
|
|
||||||
start = connection.startSpace.position +
|
|
||||||
const Offset(150, 30); // Right center
|
|
||||||
end = connection.endSpace.position + const Offset(0, 30); // Left center
|
|
||||||
|
|
||||||
canvas.drawLine(start, end, paint);
|
|
||||||
} else if (connection.direction == 'left') {
|
|
||||||
start =
|
|
||||||
connection.startSpace.position + const Offset(0, 30); // Left center
|
|
||||||
end = connection.endSpace.position +
|
|
||||||
const Offset(150, 30); // Right center
|
|
||||||
|
|
||||||
canvas.drawLine(start, end, paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
final dotPaint = Paint()..color = ColorsManager.blackColor;
|
final dotPaint = Paint()..color = ColorsManager.blackColor;
|
||||||
canvas.drawCircle(start, 5, dotPaint); // Start dot
|
canvas.drawCircle(start, 5, dotPaint); // Start dot
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
|
||||||
import 'package:syncrow_web/common/edit_chip.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
||||||
@ -9,6 +7,11 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_pr
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/devices_part_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/icon_choose_part_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_model_linking_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/space_name_textfield_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/create_space_widgets/sub_space_part_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
||||||
@ -16,8 +19,6 @@ import 'package:syncrow_web/pages/spaces_management/helper/space_helper.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
||||||
@ -82,8 +83,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
selectedIcon = widget.icon ?? Assets.location;
|
selectedIcon = widget.icon ?? Assets.location;
|
||||||
nameController = TextEditingController(text: widget.name ?? '');
|
nameController = TextEditingController(text: widget.name ?? '');
|
||||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
selectedProducts =
|
||||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||||
|
isOkButtonEnabled =
|
||||||
|
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||||
if (widget.currentSpaceModel != null) {
|
if (widget.currentSpaceModel != null) {
|
||||||
subspaces = [];
|
subspaces = [];
|
||||||
tags = [];
|
tags = [];
|
||||||
@ -96,13 +99,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isSpaceModelDisabled =
|
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||||
(tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty);
|
subspaces != null && subspaces!.isNotEmpty);
|
||||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||||
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'),
|
title: widget.isEdit
|
||||||
|
? const Text('Edit Space')
|
||||||
|
: const Text('Create New Space'),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: screenWidth * 0.5,
|
width: screenWidth * 0.5,
|
||||||
@ -112,50 +117,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 1,
|
flex: 1,
|
||||||
child: Column(
|
child: IconChoosePartWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
selectedIcon: selectedIcon,
|
||||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
showIconSelection: _showIconSelectionDialog,
|
||||||
children: [
|
screenWidth: screenWidth,
|
||||||
const SizedBox(height: 50),
|
|
||||||
Stack(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
width: screenWidth * 0.1,
|
|
||||||
height: screenWidth * 0.1,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SvgPicture.asset(
|
|
||||||
selectedIcon,
|
|
||||||
width: screenWidth * 0.04,
|
|
||||||
height: screenWidth * 0.04,
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: 20,
|
|
||||||
right: 20,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: _showIconSelectionDialog,
|
|
||||||
child: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
Assets.iconEdit,
|
|
||||||
width: 16,
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
@ -164,11 +129,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SpaceNameTextfieldWidget(
|
||||||
width: screenWidth * 0.25,
|
isNameFieldExist: isNameFieldExist,
|
||||||
child: TextField(
|
isNameFieldInvalid: isNameFieldInvalid,
|
||||||
controller: nameController,
|
nameController: nameController,
|
||||||
onChanged: (value) {
|
screenWidth: screenWidth,
|
||||||
|
onChange: (value) {
|
||||||
enteredName = value.trim();
|
enteredName = value.trim();
|
||||||
setState(() {
|
setState(() {
|
||||||
isNameFieldExist = false;
|
isNameFieldExist = false;
|
||||||
@ -187,273 +153,88 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Please enter the name',
|
|
||||||
hintStyle: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.lightGrayColor),
|
|
||||||
filled: true,
|
|
||||||
fillColor: ColorsManager.boxColor,
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: isNameFieldInvalid || isNameFieldExist
|
|
||||||
? ColorsManager.red
|
|
||||||
: ColorsManager.boxColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
borderSide: const BorderSide(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isNameFieldInvalid)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
'*Space name should not be empty.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.red),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isNameFieldExist)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
|
||||||
'*Name already exist',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.red),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
selectedSpaceModel == null
|
// SpaceModelLinkingWidget(
|
||||||
? TextButton(
|
// isSpaceModelDisabled: true,
|
||||||
style: TextButton.styleFrom(
|
// // isSpaceModelDisabled,
|
||||||
padding: EdgeInsets.zero,
|
// onPressed: () {
|
||||||
),
|
// isSpaceModelDisabled
|
||||||
|
// ? null
|
||||||
|
// : _showLinkSpaceModelDialog(context);
|
||||||
|
// },
|
||||||
|
// onDeleted: () => setState(() {
|
||||||
|
// selectedSpaceModel = null;
|
||||||
|
// subspaces = widget.subspaces ?? [];
|
||||||
|
// tags = widget.tags ?? [];
|
||||||
|
// }),
|
||||||
|
// screenWidth: screenWidth,
|
||||||
|
// selectedSpaceModel: selectedSpaceModel,
|
||||||
|
// ),
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
// Row(
|
||||||
|
// children: [
|
||||||
|
// const Expanded(
|
||||||
|
// child: Divider(
|
||||||
|
// color: ColorsManager.neutralGray,
|
||||||
|
// thickness: 1.0,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Padding(
|
||||||
|
// padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
||||||
|
// child: Text(
|
||||||
|
// 'OR',
|
||||||
|
// style: Theme.of(context)
|
||||||
|
// .textTheme
|
||||||
|
// .bodyMedium
|
||||||
|
// ?.copyWith(fontWeight: FontWeight.bold),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// const Expanded(
|
||||||
|
// child: Divider(
|
||||||
|
// color: ColorsManager.neutralGray,
|
||||||
|
// thickness: 1.0,
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
const SizedBox(height: 25),
|
||||||
|
SubSpacePartWidget(
|
||||||
|
subspaces: subspaces,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context);
|
|
||||||
},
|
|
||||||
child: ButtonContentWidget(
|
|
||||||
svgAssets: Assets.link,
|
|
||||||
label: 'Link a space model',
|
|
||||||
disabled: isSpaceModelDisabled,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
width: screenWidth * 0.25,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
Chip(
|
|
||||||
label: Text(
|
|
||||||
selectedSpaceModel?.modelName ?? '',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.transparentColor,
|
|
||||||
width: 0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
deleteIcon: Container(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
size: 16,
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onDeleted: () => setState(() {
|
|
||||||
this.selectedSpaceModel = null;
|
|
||||||
subspaces = widget.subspaces ?? [];
|
|
||||||
tags = widget.tags ?? [];
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Expanded(
|
|
||||||
child: Divider(
|
|
||||||
color: ColorsManager.neutralGray,
|
|
||||||
thickness: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6.0),
|
|
||||||
child: Text(
|
|
||||||
'OR',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium
|
|
||||||
?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Expanded(
|
|
||||||
child: Divider(
|
|
||||||
color: ColorsManager.neutralGray,
|
|
||||||
thickness: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 25),
|
|
||||||
subspaces == null || subspaces!.isEmpty
|
|
||||||
? TextButton(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
overlayColor: ColorsManager.transparentColor,
|
|
||||||
),
|
|
||||||
onPressed: () async {
|
|
||||||
isTagsAndSubspaceModelDisabled
|
isTagsAndSubspaceModelDisabled
|
||||||
? null
|
? null
|
||||||
: _showSubSpaceDialog(
|
: _showSubSpaceDialog(
|
||||||
context, enteredName, [], false, widget.products, subspaces);
|
context,
|
||||||
},
|
enteredName,
|
||||||
child: ButtonContentWidget(
|
[],
|
||||||
icon: Icons.add,
|
false,
|
||||||
label: 'Create Sub Space',
|
widget.products,
|
||||||
disabled: isTagsAndSubspaceModelDisabled,
|
subspaces,
|
||||||
),
|
);
|
||||||
)
|
},
|
||||||
: SizedBox(
|
isTagsAndSubspaceModelDisabled:
|
||||||
width: screenWidth * 0.25,
|
isTagsAndSubspaceModelDisabled,
|
||||||
child: Container(
|
screenWidth: screenWidth,
|
||||||
padding: const EdgeInsets.all(8.0),
|
editChipOnTap: () async {
|
||||||
decoration: BoxDecoration(
|
_showSubSpaceDialog(
|
||||||
color: ColorsManager.textFieldGreyColor,
|
context,
|
||||||
borderRadius: BorderRadius.circular(15),
|
enteredName,
|
||||||
border: Border.all(
|
[],
|
||||||
color: ColorsManager.textFieldGreyColor,
|
true,
|
||||||
width: 3.0, // Border width
|
widget.products,
|
||||||
),
|
subspaces,
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
if (subspaces != null)
|
|
||||||
...subspaces!.map((subspace) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
SubspaceNameDisplayWidget(
|
|
||||||
text: subspace.subspaceName,
|
|
||||||
validateName: (updatedName) {
|
|
||||||
bool nameExists = subspaces!.any((s) {
|
|
||||||
bool isSameId = s.internalId == subspace.internalId;
|
|
||||||
bool isSameName =
|
|
||||||
s.subspaceName.trim().toLowerCase() ==
|
|
||||||
updatedName.trim().toLowerCase();
|
|
||||||
|
|
||||||
return !isSameId && isSameName;
|
|
||||||
});
|
|
||||||
|
|
||||||
return !nameExists;
|
|
||||||
},
|
|
||||||
onNameChanged: (updatedName) {
|
|
||||||
setState(() {
|
|
||||||
subspace.subspaceName = updatedName;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}),
|
|
||||||
EditChip(
|
|
||||||
onTap: () async {
|
|
||||||
_showSubSpaceDialog(context, enteredName, [], true,
|
|
||||||
widget.products, subspaces);
|
|
||||||
},
|
},
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
(tags?.isNotEmpty == true ||
|
DevicesPartWidget(
|
||||||
subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true)
|
tags: tags,
|
||||||
? SizedBox(
|
subspaces: subspaces,
|
||||||
width: screenWidth * 0.25,
|
screenWidth: screenWidth,
|
||||||
child: Container(
|
isTagsAndSubspaceModelDisabled:
|
||||||
padding: const EdgeInsets.all(8.0),
|
isTagsAndSubspaceModelDisabled,
|
||||||
decoration: BoxDecoration(
|
onEditChip: () async {
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.textFieldGreyColor,
|
|
||||||
width: 3.0, // Border width
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8.0,
|
|
||||||
runSpacing: 8.0,
|
|
||||||
children: [
|
|
||||||
// Combine tags from spaceModel and subspaces
|
|
||||||
...TagHelper.groupTags([
|
|
||||||
...?tags,
|
|
||||||
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
|
||||||
]).entries.map(
|
|
||||||
(entry) => Chip(
|
|
||||||
avatar: SizedBox(
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
'x${entry.value}', // Show count
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall
|
|
||||||
?.copyWith(color: ColorsManager.spaceColor),
|
|
||||||
),
|
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
EditChip(onTap: () async {
|
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AssignTagDialog(
|
builder: (context) => AssignTagDialog(
|
||||||
@ -476,13 +257,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
})
|
},
|
||||||
],
|
onTextButtonPressed: () {
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
isTagsAndSubspaceModelDisabled
|
isTagsAndSubspaceModelDisabled
|
||||||
? null
|
? null
|
||||||
: _showTagCreateDialog(
|
: _showTagCreateDialog(
|
||||||
@ -492,14 +268,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
widget.products,
|
widget.products,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
)
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
child: ButtonContentWidget(
|
|
||||||
icon: Icons.add,
|
|
||||||
label: 'Add Devices',
|
|
||||||
disabled: isTagsAndSubspaceModelDisabled,
|
|
||||||
))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -529,17 +298,32 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
} else if (isNameFieldExist) {
|
} else if (isNameFieldExist) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
String newName = enteredName.isNotEmpty
|
||||||
|
? enteredName
|
||||||
|
: (widget.name ?? '');
|
||||||
if (newName.isNotEmpty) {
|
if (newName.isNotEmpty) {
|
||||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts,
|
if (tags != null && tags!.isNotEmpty) {
|
||||||
selectedSpaceModel, subspaces, tags);
|
if (tags!.any(
|
||||||
|
(tag) => tag.uuid == null || tag.uuid!.isEmpty,
|
||||||
|
)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widget.onCreateSpace(
|
||||||
|
newName,
|
||||||
|
selectedIcon,
|
||||||
|
selectedProducts,
|
||||||
|
selectedSpaceModel,
|
||||||
|
subspaces,
|
||||||
|
tags);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
backgroundColor:
|
backgroundColor: isOkButtonEnabled
|
||||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
? ColorsManager.secondaryColor
|
||||||
|
: ColorsManager.grayColor,
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
@ -550,6 +334,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//dialooogggs
|
||||||
void _showIconSelectionDialog() {
|
void _showIconSelectionDialog() {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -586,26 +371,50 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
void _showSubSpaceDialog(
|
||||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
BuildContext context,
|
||||||
|
String name,
|
||||||
|
final List<Tag>? spaceTags,
|
||||||
|
bool isEdit,
|
||||||
|
List<ProductModel>? products,
|
||||||
|
final List<SubspaceModel>? existingSubSpaces,
|
||||||
|
) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CreateSubSpaceDialog(
|
return CreateSubSpaceDialog(
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces',
|
||||||
products: products,
|
products: products,
|
||||||
existingSubSpaces: existingSubSpaces,
|
existingSubSpaces: existingSubSpaces,
|
||||||
onSave: (slectedSubspaces) {
|
onSave: (slectedSubspaces, updatedSubSpaces) {
|
||||||
final List<Tag> tagsToAppendToSpace = [];
|
final List<Tag> tagsToAppendToSpace = [];
|
||||||
|
|
||||||
if (slectedSubspaces != null) {
|
if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) {
|
||||||
final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet();
|
final updatedIds =
|
||||||
|
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||||
if (existingSubSpaces != null) {
|
if (existingSubSpaces != null) {
|
||||||
final deletedSubspaces =
|
final deletedSubspaces = existingSubSpaces
|
||||||
existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList();
|
.where((s) => !updatedIds.contains(s.internalId))
|
||||||
|
.toList();
|
||||||
for (var s in deletedSubspaces) {
|
for (var s in deletedSubspaces) {
|
||||||
if (s.tags != null) {
|
if (s.tags != null) {
|
||||||
|
s.tags!.forEach(
|
||||||
|
(tag) => tag.location = null,
|
||||||
|
);
|
||||||
|
tagsToAppendToSpace.addAll(s.tags!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (existingSubSpaces != null) {
|
||||||
|
final deletedSubspaces = existingSubSpaces;
|
||||||
|
|
||||||
|
for (var s in deletedSubspaces) {
|
||||||
|
if (s.tags != null) {
|
||||||
|
s.tags!.forEach(
|
||||||
|
(tag) => tag.location = null,
|
||||||
|
);
|
||||||
tagsToAppendToSpace.addAll(s.tags!);
|
tagsToAppendToSpace.addAll(s.tags!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -623,15 +432,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagCreateDialog(
|
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||||
BuildContext context, String name, bool isEdit, List<ProductModel>? products) {
|
List<ProductModel>? products) {
|
||||||
isEdit
|
isEdit
|
||||||
? showDialog(
|
? showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AssignTagDialog(
|
return AssignTagDialog(
|
||||||
title: 'Edit Device',
|
title: 'Edit Device',
|
||||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
@ -646,7 +456,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +481,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
allTags: widget.allTags,
|
allTags: widget.allTags,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
initialSelectedProducts:
|
initialSelectedProducts:
|
||||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = selectedSpaceTags;
|
tags = selectedSpaceTags;
|
||||||
@ -680,7 +492,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
final int index;
|
final int index;
|
||||||
final String direction;
|
final String direction;
|
||||||
final Offset offset;
|
final Offset offset;
|
||||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
final Function(int index, Offset newPosition) onButtonTap;
|
||||||
|
|
||||||
const PlusButtonWidget({
|
const PlusButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
@ -17,26 +17,9 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return GestureDetector(
|
||||||
left: offset.dx,
|
|
||||||
top: offset.dy,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Offset newPosition;
|
onButtonTap(index, const Offset(0, 150));
|
||||||
switch (direction) {
|
|
||||||
case 'left':
|
|
||||||
newPosition = const Offset(-200, 0);
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
newPosition = const Offset(200, 0);
|
|
||||||
break;
|
|
||||||
case 'down':
|
|
||||||
newPosition = const Offset(0, 150);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
newPosition = Offset.zero;
|
|
||||||
}
|
|
||||||
onButtonTap(index, newPosition, direction);
|
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 30,
|
width: 30,
|
||||||
@ -45,7 +28,10 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
color: ColorsManager.spaceColor,
|
color: ColorsManager.spaceColor,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
child: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -7,7 +7,7 @@ class SpaceCardWidget extends StatelessWidget {
|
|||||||
final Offset position;
|
final Offset position;
|
||||||
final bool isHovered;
|
final bool isHovered;
|
||||||
final Function(int index, bool isHovered) onHoverChanged;
|
final Function(int index, bool isHovered) onHoverChanged;
|
||||||
final Function(int index, Offset newPosition, String direction) onButtonTap;
|
final Function(int index, Offset newPosition) onButtonTap;
|
||||||
final Widget Function(int index) buildSpaceContainer;
|
final Widget Function(int index) buildSpaceContainer;
|
||||||
final ValueChanged<Offset> onPositionChanged;
|
final ValueChanged<Offset> onPositionChanged;
|
||||||
|
|
||||||
@ -25,35 +25,34 @@ class SpaceCardWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return MouseRegion(
|
||||||
behavior: HitTestBehavior.opaque,
|
onEnter: (_) => onHoverChanged(index, true),
|
||||||
onPanUpdate: (details) {
|
onExit: (_) => onHoverChanged(index, false),
|
||||||
// Call the provided callback to update the position
|
child: SizedBox(
|
||||||
final newPosition = position + details.delta;
|
width: 140, // Make sure this covers both card and plus button
|
||||||
onPositionChanged(newPosition);
|
height: 90,
|
||||||
},
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (_) {
|
|
||||||
// Call the provided callback to handle hover state
|
|
||||||
onHoverChanged(index, true);
|
|
||||||
},
|
|
||||||
onExit: (_) {
|
|
||||||
// Call the provided callback to handle hover state
|
|
||||||
onHoverChanged(index, false);
|
|
||||||
},
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip
|
clipBehavior: Clip.none,
|
||||||
.none, // Allow hovering elements to be displayed outside the boundary
|
|
||||||
children: [
|
children: [
|
||||||
buildSpaceContainer(index), // Build the space container
|
// Main card
|
||||||
if (isHovered) ...[
|
Container(
|
||||||
PlusButtonWidget(
|
width: 140,
|
||||||
|
height: 80,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: buildSpaceContainer(index),
|
||||||
|
),
|
||||||
|
// Plus button (NO inner Positioned!)
|
||||||
|
if (isHovered)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: PlusButtonWidget(
|
||||||
index: index,
|
index: index,
|
||||||
direction: 'down',
|
direction: 'down',
|
||||||
offset: const Offset(63, 50),
|
offset: Offset.zero,
|
||||||
onButtonTap: onButtonTap,
|
onButtonTap: onButtonTap,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -13,7 +13,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final existingTagCounts = <String, int>{};
|
final existingTagCounts = <String, int>{};
|
||||||
for (var tag in initialTags) {
|
for (var tag in initialTags) {
|
||||||
if (tag.product != null) {
|
if (tag.product != null) {
|
||||||
existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
existingTagCounts[tag.product!.uuid] =
|
||||||
|
(existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,14 +23,17 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
for (var selectedProduct in event.addedProducts) {
|
for (var selectedProduct in event.addedProducts) {
|
||||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||||
|
|
||||||
if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) {
|
if (selectedProduct.count == 0 ||
|
||||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
selectedProduct.count <= existingCount) {
|
||||||
|
tags.addAll(initialTags
|
||||||
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final missingCount = selectedProduct.count - existingCount;
|
final missingCount = selectedProduct.count - existingCount;
|
||||||
|
|
||||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
tags.addAll(initialTags
|
||||||
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
|
|
||||||
if (missingCount > 0) {
|
if (missingCount > 0) {
|
||||||
tags.addAll(List.generate(
|
tags.addAll(List.generate(
|
||||||
@ -85,7 +89,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final tags = List<Tag>.from(currentState.tags);
|
final tags = List<Tag>.from(currentState.tags);
|
||||||
|
|
||||||
// Update the location
|
// Update the location
|
||||||
tags[event.index] = tags[event.index].copyWith(location: event.location);
|
tags[event.index] =
|
||||||
|
tags[event.index].copyWith(location: event.location);
|
||||||
|
|
||||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||||
|
|
||||||
@ -117,7 +122,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||||
final tags = List<Tag>.from(currentState.tags)..remove(event.tagToDelete);
|
final tags = List<Tag>.from(currentState.tags)
|
||||||
|
..remove(event.tagToDelete);
|
||||||
|
|
||||||
// Recalculate available tags
|
// Recalculate available tags
|
||||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||||
@ -141,8 +147,10 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
|
|
||||||
// Get validation error for duplicate tags
|
// Get validation error for duplicate tags
|
||||||
String? _getValidationError(List<Tag> tags) {
|
String? _getValidationError(List<Tag> tags) {
|
||||||
final nonEmptyTags =
|
final nonEmptyTags = tags
|
||||||
tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList();
|
.map((tag) => tag.tag?.trim() ?? '')
|
||||||
|
.where((tag) => tag.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
final duplicateTags = nonEmptyTags
|
final duplicateTags = nonEmptyTags
|
||||||
.fold<Map<String, int>>({}, (map, tag) {
|
.fold<Map<String, int>>({}, (map, tag) {
|
||||||
@ -168,9 +176,11 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
.toSet();
|
.toSet();
|
||||||
|
|
||||||
final availableTags = allTags
|
final availableTags = allTags
|
||||||
.where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
.where((tag) =>
|
||||||
|
tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return availableTags;
|
return projectTags;
|
||||||
|
// availableTags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
@ -12,9 +7,10 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/widgets/assign_tags_tables_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
|
||||||
|
import 'widgets/save_add_device_row_widget.dart';
|
||||||
|
|
||||||
class AssignTagDialog extends StatelessWidget {
|
class AssignTagDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -29,7 +25,7 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
final List<Tag> projectTags;
|
final List<Tag> projectTags;
|
||||||
|
|
||||||
const AssignTagDialog(
|
const AssignTagDialog(
|
||||||
{Key? key,
|
{super.key,
|
||||||
required this.products,
|
required this.products,
|
||||||
required this.subspaces,
|
required this.subspaces,
|
||||||
required this.addedProducts,
|
required this.addedProducts,
|
||||||
@ -39,13 +35,14 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
required this.spaceName,
|
required this.spaceName,
|
||||||
required this.title,
|
required this.title,
|
||||||
this.onSave,
|
this.onSave,
|
||||||
required this.projectTags})
|
required this.projectTags});
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<String> locations =
|
final List<String> locations = (subspaces ?? [])
|
||||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
.map((subspace) => subspace.subspaceName)
|
||||||
|
.toList()
|
||||||
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagBloc(projectTags)
|
create: (_) => AssignTagBloc(projectTags)
|
||||||
@ -67,131 +64,31 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
AssignTagsTable(
|
||||||
borderRadius: BorderRadius.circular(20),
|
controllers: controllers,
|
||||||
child: DataTable(
|
locations: locations,
|
||||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
tags: state.tags,
|
||||||
key: ValueKey(state.tags.length),
|
updatedTags: state.updatedTags,
|
||||||
border: TableBorder.all(
|
onDeleteDevice: ({required index, required tag}) {
|
||||||
color: ColorsManager.dataHeaderGrey,
|
context
|
||||||
width: 1,
|
.read<AssignTagBloc>()
|
||||||
borderRadius: BorderRadius.circular(20),
|
.add(DeleteTag(tagToDelete: tag, tags: state.tags));
|
||||||
),
|
|
||||||
columns: [
|
|
||||||
DataColumn(
|
|
||||||
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Device', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
numeric: false,
|
|
||||||
label: Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label:
|
|
||||||
Text('Location', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
],
|
|
||||||
rows: state.tags.isEmpty
|
|
||||||
? [
|
|
||||||
DataRow(cells: [
|
|
||||||
DataCell(
|
|
||||||
Center(
|
|
||||||
child: Text('No Data Available',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
])
|
|
||||||
]
|
|
||||||
: List.generate(state.tags.length, (index) {
|
|
||||||
final tag = state.tags[index];
|
|
||||||
final controller = controllers[index];
|
|
||||||
|
|
||||||
return DataRow(
|
|
||||||
cells: [
|
|
||||||
DataCell(Text((index + 1).toString())),
|
|
||||||
DataCell(
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
tag.product?.name ?? 'Unknown',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Container(
|
|
||||||
width: 20.0,
|
|
||||||
height: 20.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
color: ColorsManager.lightGreyColor,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.read<AssignTagBloc>().add(
|
|
||||||
DeleteTag(tagToDelete: tag, tags: state.tags));
|
|
||||||
|
|
||||||
controllers.removeAt(index);
|
controllers.removeAt(index);
|
||||||
},
|
},
|
||||||
tooltip: 'Delete Tag',
|
onTagDropDownSelected: ({required index, required tag}) {
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
Container(
|
|
||||||
alignment:
|
|
||||||
Alignment.centerLeft, // Align cell content to the left
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: TagDialogTextfieldDropdown(
|
|
||||||
key: ValueKey('dropdown_${const Uuid().v4()}_$index'),
|
|
||||||
items: state.updatedTags,
|
|
||||||
product: tag.product?.uuid ?? 'Unknown',
|
|
||||||
initialValue: tag,
|
|
||||||
onSelected: (value) {
|
|
||||||
controller.text = value.tag ?? '';
|
|
||||||
context.read<AssignTagBloc>().add(UpdateTagEvent(
|
context.read<AssignTagBloc>().add(UpdateTagEvent(
|
||||||
index: index,
|
index: index,
|
||||||
tag: value,
|
tag: tag,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
),
|
onLocationDropDownSelected: (
|
||||||
),
|
{required index, required location}) {
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: DialogDropdown(
|
|
||||||
items: locations,
|
|
||||||
selectedValue: tag.location ?? 'Main Space',
|
|
||||||
onSelected: (value) {
|
|
||||||
context.read<AssignTagBloc>().add(UpdateLocation(
|
context.read<AssignTagBloc>().add(UpdateLocation(
|
||||||
index: index,
|
index: index,
|
||||||
location: value,
|
location: location,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(state.errorMessage!,
|
||||||
@ -203,69 +100,15 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
SaveAddDeviceRowWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
subspaces: subspaces,
|
||||||
children: [
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Builder(
|
|
||||||
builder: (buttonContext) => CancelButton(
|
|
||||||
label: 'Add New Device',
|
|
||||||
onPressed: () async {
|
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
|
||||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
|
||||||
|
|
||||||
final processedTags = result['updatedTags'] as List<Tag>;
|
|
||||||
final processedSubspaces =
|
|
||||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AddDeviceTypeWidget(
|
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: processedSubspaces,
|
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
initialSelectedProducts:
|
|
||||||
TagHelper.createInitialSelectedProductsForTags(
|
|
||||||
processedTags, processedSubspaces),
|
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
spaceTags: processedTags,
|
|
||||||
isCreate: false,
|
|
||||||
onSave: onSave,
|
onSave: onSave,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
),
|
tags: state.tags,
|
||||||
);
|
isSaveEnabled: state.isSaveEnabled,
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
borderRadius: 10,
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
foregroundColor: state.isSaveEnabled
|
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.whiteColorsWithOpacity,
|
|
||||||
onPressed: state.isSaveEnabled
|
|
||||||
? () async {
|
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
|
||||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
|
||||||
|
|
||||||
final processedTags = result['updatedTags'] as List<Tag>;
|
|
||||||
final processedSubspaces =
|
|
||||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
|
||||||
onSave?.call(processedTags, processedSubspaces);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: const Text('Save'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
import '../../../add_device_type/views/add_device_type_widget.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
|
||||||
|
class SaveAddDeviceRowWidget extends StatelessWidget {
|
||||||
|
const SaveAddDeviceRowWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.products,
|
||||||
|
required this.projectTags,
|
||||||
|
required this.spaceName,
|
||||||
|
required this.onSave,
|
||||||
|
required this.allTags,
|
||||||
|
required this.tags,
|
||||||
|
required this.isSaveEnabled,
|
||||||
|
});
|
||||||
|
final List<Tag> tags;
|
||||||
|
final List<SubspaceModel>? subspaces;
|
||||||
|
final List<ProductModel>? products;
|
||||||
|
final List<Tag> projectTags;
|
||||||
|
final String spaceName;
|
||||||
|
final Function(List<Tag> p1, List<SubspaceModel>? p2)? onSave;
|
||||||
|
final List<String>? allTags;
|
||||||
|
final bool isSaveEnabled;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Builder(
|
||||||
|
builder: (buttonContext) => CancelButton(
|
||||||
|
label: 'Add New Device',
|
||||||
|
onPressed: () async {
|
||||||
|
final updatedTags = List<Tag>.from(tags);
|
||||||
|
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||||
|
|
||||||
|
final processedTags = result['updatedTags'] as List<Tag>;
|
||||||
|
final processedSubspaces = List<SubspaceModel>.from(
|
||||||
|
result['subspaces'] as List<dynamic>);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AddDeviceTypeWidget(
|
||||||
|
products: products,
|
||||||
|
subspaces: processedSubspaces,
|
||||||
|
projectTags: projectTags,
|
||||||
|
initialSelectedProducts:
|
||||||
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
processedTags, processedSubspaces),
|
||||||
|
spaceName: spaceName,
|
||||||
|
spaceTags: processedTags,
|
||||||
|
isCreate: false,
|
||||||
|
onSave: onSave,
|
||||||
|
allTags: allTags,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: DefaultButton(
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
|
foregroundColor: isSaveEnabled
|
||||||
|
? ColorsManager.whiteColors
|
||||||
|
: ColorsManager.whiteColorsWithOpacity,
|
||||||
|
onPressed: isSaveEnabled
|
||||||
|
? () async {
|
||||||
|
final updatedTags = List<Tag>.from(tags);
|
||||||
|
final result =
|
||||||
|
TagHelper.processTags(updatedTags, subspaces);
|
||||||
|
|
||||||
|
final processedTags = result['updatedTags'] as List<Tag>;
|
||||||
|
final processedSubspaces = List<SubspaceModel>.from(
|
||||||
|
result['subspaces'] as List<dynamic>);
|
||||||
|
onSave?.call(processedTags, processedSubspaces);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: const Text('Save'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
@ -12,11 +8,10 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assig
|
|||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
|
||||||
import 'package:uuid/uuid.dart';
|
import 'widgets/RowOfCancelSaveWidget.dart';
|
||||||
|
import 'widgets/assign_tags_tables_widget.dart';
|
||||||
|
|
||||||
class AssignTagModelsDialog extends StatelessWidget {
|
class AssignTagModelsDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -53,8 +48,10 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<String> locations =
|
final List<String> locations = (subspaces ?? [])
|
||||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
.map((subspace) => subspace.subspaceName)
|
||||||
|
.toList()
|
||||||
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagModelBloc(projectTags)
|
create: (_) => AssignTagModelBloc(projectTags)
|
||||||
@ -78,137 +75,38 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
content: SingleChildScrollView(
|
content: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
ClipRRect(
|
AssignTagsTable(
|
||||||
borderRadius: BorderRadius.circular(20),
|
controllers: controllers,
|
||||||
child: DataTable(
|
locations: locations,
|
||||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
tags: state.tags,
|
||||||
key: ValueKey(state.tags.length),
|
updatedTags: state.updatedTags,
|
||||||
border: TableBorder.all(
|
onDeleteDevice: ({required index, required tag}) {
|
||||||
color: ColorsManager.dataHeaderGrey,
|
|
||||||
width: 1,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
columns: [
|
|
||||||
DataColumn(
|
|
||||||
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Device',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
numeric: false,
|
|
||||||
label:
|
|
||||||
Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
DataColumn(
|
|
||||||
label: Text('Location',
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium)),
|
|
||||||
],
|
|
||||||
rows: state.tags.isEmpty
|
|
||||||
? [
|
|
||||||
DataRow(cells: [
|
|
||||||
DataCell(
|
|
||||||
Center(
|
|
||||||
child: Text('No Devices Available',
|
|
||||||
style:
|
|
||||||
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
const DataCell(SizedBox()),
|
|
||||||
])
|
|
||||||
]
|
|
||||||
: List.generate(state.tags.length, (index) {
|
|
||||||
final tag = state.tags[index];
|
|
||||||
final controller = controllers[index];
|
|
||||||
|
|
||||||
return DataRow(
|
|
||||||
cells: [
|
|
||||||
DataCell(Text((index + 1).toString())),
|
|
||||||
DataCell(
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
tag.product?.name ?? 'Unknown',
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
)),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Container(
|
|
||||||
width: 20.0,
|
|
||||||
height: 20.0,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.close,
|
|
||||||
color: ColorsManager.lightGreyColor,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
context.read<AssignTagModelBloc>().add(
|
|
||||||
DeleteTagModel(
|
|
||||||
tagToDelete: tag, tags: state.tags));
|
|
||||||
controllers.removeAt(index);
|
|
||||||
},
|
|
||||||
tooltip: 'Delete Tag',
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
constraints: const BoxConstraints(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
Container(
|
|
||||||
alignment: Alignment
|
|
||||||
.centerLeft, // Align cell content to the left
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: TagDialogTextfieldDropdown(
|
|
||||||
key: ValueKey(
|
|
||||||
'dropdown_${const Uuid().v4()}_$index'),
|
|
||||||
product: tag.product?.uuid ?? 'Unknown',
|
|
||||||
items: state.updatedTags,
|
|
||||||
initialValue: tag,
|
|
||||||
onSelected: (value) {
|
|
||||||
controller.text = value.tag ?? '';
|
|
||||||
context.read<AssignTagModelBloc>().add(UpdateTag(
|
|
||||||
index: index,
|
|
||||||
tag: value,
|
|
||||||
));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DataCell(
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: DialogDropdown(
|
|
||||||
items: locations,
|
|
||||||
selectedValue: tag.location ?? 'Main Space',
|
|
||||||
onSelected: (value) {
|
|
||||||
context
|
context
|
||||||
.read<AssignTagModelBloc>()
|
.read<AssignTagModelBloc>()
|
||||||
.add(UpdateLocation(
|
.add(DeleteTagModel(
|
||||||
index: index,
|
tagToDelete: tag,
|
||||||
location: value,
|
tags: state.tags,
|
||||||
));
|
));
|
||||||
|
controllers.removeAt(index);
|
||||||
},
|
},
|
||||||
)),
|
onTagDropDownSelected: (
|
||||||
|
{required index, required tag}) {
|
||||||
|
context.read<AssignTagModelBloc>().add(
|
||||||
|
UpdateTag(
|
||||||
|
index: index,
|
||||||
|
tag: tag,
|
||||||
),
|
),
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
|
onLocationDropDownSelected: (
|
||||||
|
{required index, required location}) {
|
||||||
|
context.read<AssignTagModelBloc>().add(
|
||||||
|
UpdateLocation(
|
||||||
|
index: index,
|
||||||
|
location: location,
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
if (state.errorMessage != null)
|
if (state.errorMessage != null)
|
||||||
Text(state.errorMessage!,
|
Text(state.errorMessage!,
|
||||||
@ -220,101 +118,16 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
RowOfSaveCancelWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
subspaces: subspaces,
|
||||||
children: [
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Builder(
|
|
||||||
builder: (buttonContext) => CancelButton(
|
|
||||||
label: 'Add New Device',
|
|
||||||
onPressed: () async {
|
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
|
||||||
final result =
|
|
||||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
|
||||||
|
|
||||||
final processedTags = result['updatedTags'] as List<Tag>;
|
|
||||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
|
||||||
result['subspaces'] as List<dynamic>);
|
|
||||||
|
|
||||||
if (context.mounted) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
|
|
||||||
await showDialog<bool>(
|
|
||||||
barrierDismissible: false,
|
|
||||||
context: context,
|
|
||||||
builder: (dialogContext) => AddDeviceTypeModelWidget(
|
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: processedSubspaces,
|
|
||||||
isCreate: false,
|
|
||||||
initialSelectedProducts:
|
|
||||||
TagHelper.createInitialSelectedProducts(
|
|
||||||
processedTags, processedSubspaces),
|
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
otherSpaceModels: otherSpaceModels,
|
otherSpaceModels: otherSpaceModels,
|
||||||
spaceTagModels: processedTags,
|
|
||||||
pageContext: pageContext,
|
pageContext: pageContext,
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
spaceModel: SpaceTemplateModel(
|
spaceModel: spaceModel,
|
||||||
modelName: spaceName,
|
|
||||||
tags: updatedTags,
|
|
||||||
uuid: spaceModel?.uuid,
|
|
||||||
internalId: spaceModel?.internalId,
|
|
||||||
subspaceModels: processedSubspaces)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
borderRadius: 10,
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
foregroundColor: state.isSaveEnabled
|
|
||||||
? ColorsManager.whiteColors
|
|
||||||
: ColorsManager.whiteColorsWithOpacity,
|
|
||||||
onPressed: state.isSaveEnabled
|
|
||||||
? () async {
|
|
||||||
final updatedTags = List<Tag>.from(state.tags);
|
|
||||||
|
|
||||||
final result =
|
|
||||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
|
||||||
|
|
||||||
final processedTags = result['updatedTags'] as List<Tag>;
|
|
||||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
|
||||||
result['subspaces'] as List<dynamic>);
|
|
||||||
|
|
||||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
|
||||||
|
|
||||||
await showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext dialogContext) {
|
|
||||||
return CreateSpaceModelDialog(
|
|
||||||
products: products,
|
|
||||||
allSpaceModels: allSpaceModels,
|
allSpaceModels: allSpaceModels,
|
||||||
allTags: allTags,
|
|
||||||
projectTags: projectTags,
|
|
||||||
pageContext: pageContext,
|
|
||||||
otherSpaceModels: otherSpaceModels,
|
|
||||||
spaceModel: SpaceTemplateModel(
|
|
||||||
modelName: spaceName,
|
|
||||||
tags: processedTags,
|
|
||||||
uuid: spaceModel?.uuid,
|
|
||||||
internalId: spaceModel?.internalId,
|
|
||||||
subspaceModels: processedSubspaces),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: const Text('Save'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,152 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../../common/buttons/cancel_button.dart';
|
||||||
|
import '../../../../common/buttons/default_button.dart';
|
||||||
|
import '../../../all_spaces/model/product_model.dart';
|
||||||
|
import '../../../all_spaces/model/tag.dart';
|
||||||
|
import '../../../helper/tag_helper.dart';
|
||||||
|
import '../../../space_model/models/space_template_model.dart';
|
||||||
|
import '../../../space_model/models/subspace_template_model.dart';
|
||||||
|
import '../../../space_model/widgets/dialog/create_space_model_dialog.dart';
|
||||||
|
import '../../../tag_model/views/add_device_type_model_widget.dart';
|
||||||
|
import '../../bloc/assign_tag_model_bloc.dart';
|
||||||
|
import '../../bloc/assign_tag_model_state.dart';
|
||||||
|
|
||||||
|
class RowOfSaveCancelWidget extends StatelessWidget {
|
||||||
|
const RowOfSaveCancelWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaces,
|
||||||
|
required this.products,
|
||||||
|
required this.allTags,
|
||||||
|
required this.spaceName,
|
||||||
|
required this.otherSpaceModels,
|
||||||
|
required this.pageContext,
|
||||||
|
required this.projectTags,
|
||||||
|
required this.spaceModel,
|
||||||
|
required this.allSpaceModels,
|
||||||
|
});
|
||||||
|
|
||||||
|
final List<SubspaceTemplateModel>? subspaces;
|
||||||
|
final List<ProductModel>? products;
|
||||||
|
final List<String>? allTags;
|
||||||
|
final String spaceName;
|
||||||
|
final List<String>? otherSpaceModels;
|
||||||
|
final BuildContext? pageContext;
|
||||||
|
final List<Tag> projectTags;
|
||||||
|
final SpaceTemplateModel? spaceModel;
|
||||||
|
final List<SpaceTemplateModel>? allSpaceModels;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<AssignTagModelBloc, AssignTagModelState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is AssignTagModelLoaded) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Builder(
|
||||||
|
builder: (buttonContext) => CancelButton(
|
||||||
|
label: 'Add New Device',
|
||||||
|
onPressed: () async {
|
||||||
|
final updatedTags = List<Tag>.from(state.tags);
|
||||||
|
final result = TagHelper.updateSubspaceTagModels(
|
||||||
|
updatedTags, subspaces);
|
||||||
|
|
||||||
|
final processedTags = result['updatedTags'] as List<Tag>;
|
||||||
|
final processedSubspaces =
|
||||||
|
List<SubspaceTemplateModel>.from(
|
||||||
|
result['subspaces'] as List<dynamic>);
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
|
await showDialog<bool>(
|
||||||
|
barrierDismissible: false,
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => AddDeviceTypeModelWidget(
|
||||||
|
products: products,
|
||||||
|
subspaces: processedSubspaces,
|
||||||
|
isCreate: false,
|
||||||
|
initialSelectedProducts:
|
||||||
|
TagHelper.createInitialSelectedProducts(
|
||||||
|
processedTags, processedSubspaces),
|
||||||
|
allTags: allTags,
|
||||||
|
spaceName: spaceName,
|
||||||
|
otherSpaceModels: otherSpaceModels,
|
||||||
|
spaceTagModels: processedTags,
|
||||||
|
pageContext: pageContext,
|
||||||
|
projectTags: projectTags,
|
||||||
|
spaceModel: SpaceTemplateModel(
|
||||||
|
modelName: spaceName,
|
||||||
|
tags: updatedTags,
|
||||||
|
uuid: spaceModel?.uuid,
|
||||||
|
internalId: spaceModel?.internalId,
|
||||||
|
subspaceModels: processedSubspaces)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: DefaultButton(
|
||||||
|
borderRadius: 10,
|
||||||
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
|
foregroundColor: state.isSaveEnabled
|
||||||
|
? ColorsManager.whiteColors
|
||||||
|
: ColorsManager.whiteColorsWithOpacity,
|
||||||
|
onPressed: state.isSaveEnabled
|
||||||
|
? () async {
|
||||||
|
final updatedTags = List<Tag>.from(state.tags);
|
||||||
|
|
||||||
|
final result = TagHelper.updateSubspaceTagModels(
|
||||||
|
updatedTags, subspaces);
|
||||||
|
|
||||||
|
final processedTags =
|
||||||
|
result['updatedTags'] as List<Tag>;
|
||||||
|
final processedSubspaces =
|
||||||
|
List<SubspaceTemplateModel>.from(
|
||||||
|
result['subspaces'] as List<dynamic>);
|
||||||
|
|
||||||
|
Navigator.of(context)
|
||||||
|
.popUntil((route) => route.isFirst);
|
||||||
|
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext dialogContext) {
|
||||||
|
return CreateSpaceModelDialog(
|
||||||
|
products: products,
|
||||||
|
allSpaceModels: allSpaceModels,
|
||||||
|
allTags: allTags,
|
||||||
|
projectTags: projectTags,
|
||||||
|
pageContext: pageContext,
|
||||||
|
otherSpaceModels: otherSpaceModels,
|
||||||
|
spaceModel: SpaceTemplateModel(
|
||||||
|
modelName: spaceName,
|
||||||
|
tags: processedTags,
|
||||||
|
uuid: spaceModel?.uuid,
|
||||||
|
internalId: spaceModel?.internalId,
|
||||||
|
subspaceModels: processedSubspaces),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: const Text('Save'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,155 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
import '../../../../../common/dialog_dropdown.dart';
|
||||||
|
import '../../../../../common/tag_dialog_textfield_dropdown.dart';
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
|
||||||
|
class AssignTagsTable extends StatelessWidget {
|
||||||
|
const AssignTagsTable({
|
||||||
|
super.key,
|
||||||
|
required this.controllers,
|
||||||
|
required this.locations,
|
||||||
|
required this.tags,
|
||||||
|
required this.updatedTags,
|
||||||
|
required this.onDeleteDevice,
|
||||||
|
required this.onLocationDropDownSelected,
|
||||||
|
required this.onTagDropDownSelected,
|
||||||
|
});
|
||||||
|
final void Function({required Tag tag, required int index})
|
||||||
|
onTagDropDownSelected;
|
||||||
|
final void Function({required String location, required int index})
|
||||||
|
onLocationDropDownSelected;
|
||||||
|
final void Function({required Tag tag, required int index}) onDeleteDevice;
|
||||||
|
final List<Tag> tags;
|
||||||
|
final List<Tag> updatedTags;
|
||||||
|
final List<TextEditingController> controllers;
|
||||||
|
final List<String> locations;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
child: DataTable(
|
||||||
|
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
||||||
|
key: ValueKey(tags.length),
|
||||||
|
border: TableBorder.all(
|
||||||
|
color: ColorsManager.dataHeaderGrey,
|
||||||
|
width: 1,
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
columns: [
|
||||||
|
DataColumn(
|
||||||
|
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Device',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium)),
|
||||||
|
DataColumn(
|
||||||
|
numeric: false,
|
||||||
|
label:
|
||||||
|
Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
||||||
|
DataColumn(
|
||||||
|
label: Text('Location',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium)),
|
||||||
|
],
|
||||||
|
rows: tags.isEmpty
|
||||||
|
? [
|
||||||
|
DataRow(cells: [
|
||||||
|
DataCell(
|
||||||
|
Center(
|
||||||
|
child: Text('No Devices Available',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DataCell(SizedBox()),
|
||||||
|
const DataCell(SizedBox()),
|
||||||
|
const DataCell(SizedBox()),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
: List.generate(tags.length, (index) {
|
||||||
|
final tag = tags[index];
|
||||||
|
final controller = controllers[index];
|
||||||
|
|
||||||
|
return DataRow(
|
||||||
|
cells: [
|
||||||
|
DataCell(Text((index + 1).toString())),
|
||||||
|
DataCell(
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
tag.product?.name ?? 'Unknown',
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
)),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Container(
|
||||||
|
width: 20.0,
|
||||||
|
height: 20.0,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: ColorsManager.lightGreyColor,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
onDeleteDevice(tag: tag, index: index);
|
||||||
|
},
|
||||||
|
tooltip: 'Delete Tag',
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DataCell(
|
||||||
|
Container(
|
||||||
|
alignment: Alignment
|
||||||
|
.centerLeft, // Align cell content to the left
|
||||||
|
child: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: TagDialogTextfieldDropdown(
|
||||||
|
key: ValueKey(
|
||||||
|
'dropdown_${const Uuid().v4()}_$index'),
|
||||||
|
product: tag.product?.uuid ?? 'Unknown',
|
||||||
|
items: updatedTags,
|
||||||
|
initialValue: tag,
|
||||||
|
onSelected: (value) {
|
||||||
|
controller.text = value.tag ?? '';
|
||||||
|
onTagDropDownSelected(tag: value, index: index);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DataCell(
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: DialogDropdown(
|
||||||
|
items: locations,
|
||||||
|
selectedValue: tag.location ?? 'Main Space',
|
||||||
|
onSelected: (value) {
|
||||||
|
onLocationDropDownSelected(
|
||||||
|
location: value, index: index);
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,7 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
emit(SubSpaceState(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
updatedSubspaceModels,
|
updatedSubspaceModels,
|
||||||
errorMessage,
|
errorMessage ,
|
||||||
updatedDuplicates,
|
updatedDuplicates,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
import 'widgets/ok_cancel_sub_space_widget.dart';
|
||||||
|
import 'widgets/textfield_sub_space_dialog_widget.dart';
|
||||||
|
|
||||||
class CreateSubSpaceDialog extends StatefulWidget {
|
class CreateSubSpaceDialog extends StatefulWidget {
|
||||||
final String dialogTitle;
|
final String dialogTitle;
|
||||||
final List<SubspaceModel>? existingSubSpaces;
|
final List<SubspaceModel>? existingSubSpaces;
|
||||||
final String? spaceName;
|
final String? spaceName;
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final void Function(List<SubspaceModel>?)? onSave;
|
final void Function(
|
||||||
|
List<SubspaceModel>?, List<UpdateSubspaceModel> updatedSubSpaces)? onSave;
|
||||||
|
|
||||||
const CreateSubSpaceDialog({
|
const CreateSubSpaceDialog({
|
||||||
required this.dialogTitle,
|
required this.dialogTitle,
|
||||||
@ -33,14 +33,7 @@ class CreateSubSpaceDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
||||||
late final TextEditingController _subspaceNameController;
|
final TextEditingController _subspaceNameController = TextEditingController();
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_subspaceNameController = TextEditingController();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subspaceNameController.dispose();
|
_subspaceNameController.dispose();
|
||||||
@ -79,83 +72,26 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
Row(
|
||||||
Container(
|
|
||||||
width: context.screenWidth * 0.35,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
),
|
|
||||||
child: Wrap(
|
|
||||||
spacing: 8,
|
|
||||||
runSpacing: 8,
|
|
||||||
alignment: WrapAlignment.start,
|
|
||||||
crossAxisAlignment: WrapCrossAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
...state.subSpaces.asMap().entries.map(
|
Text(
|
||||||
(entry) {
|
'press Enter to Save',
|
||||||
final index = entry.key;
|
style: context.textTheme.headlineLarge?.copyWith(
|
||||||
final subSpace = entry.value;
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 10,
|
||||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
color: ColorsManager.grayColor,
|
||||||
|
|
||||||
final duplicateIndices = state.subSpaces
|
|
||||||
.asMap()
|
|
||||||
.entries
|
|
||||||
.where((e) =>
|
|
||||||
e.value.subspaceName.toLowerCase() == lowerName)
|
|
||||||
.map((e) => e.key)
|
|
||||||
.toList();
|
|
||||||
final isDuplicate = duplicateIndices.length > 1 &&
|
|
||||||
duplicateIndices.indexOf(index) != 0;
|
|
||||||
return SubspaceChip(
|
|
||||||
subSpace: SubspaceTemplateModel(
|
|
||||||
subspaceName: entry.value.subspaceName,
|
|
||||||
disabled: entry.value.disabled,
|
|
||||||
),
|
|
||||||
isDuplicate: isDuplicate,
|
|
||||||
onDeleted: () => context.read<SubSpaceBloc>().add(
|
|
||||||
RemoveSubSpace(subSpace),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 200,
|
|
||||||
child: TextField(
|
|
||||||
controller: _subspaceNameController,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
border: InputBorder.none,
|
|
||||||
hintText: state.subSpaces.isEmpty
|
|
||||||
? 'Please enter the name'
|
|
||||||
: null,
|
|
||||||
hintStyle: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSubmitted: (value) {
|
const SizedBox(
|
||||||
final trimmedValue = value.trim();
|
width: 5,
|
||||||
if (trimmedValue.isNotEmpty) {
|
|
||||||
context.read<SubSpaceBloc>().add(
|
|
||||||
AddSubSpace(
|
|
||||||
SubspaceModel(
|
|
||||||
subspaceName: trimmedValue,
|
|
||||||
disabled: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
_subspaceNameController.clear();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
style: context.textTheme.bodyMedium,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
const Icon(Icons.save_as_sharp, size: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextFieldSubSpaceDialogWidget(
|
||||||
|
subspaceNameController: _subspaceNameController,
|
||||||
|
subSpaces: state.subSpaces,
|
||||||
),
|
),
|
||||||
if (state.errorMessage.isNotEmpty)
|
if (state.errorMessage.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
@ -168,36 +104,10 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
OkCancelSubSpaceWidget(
|
||||||
children: [
|
subspaceNameController: _subspaceNameController,
|
||||||
Expanded(
|
errorMessage: state.errorMessage,
|
||||||
child: CancelButton(
|
onSave: widget.onSave,
|
||||||
label: 'Cancel',
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DefaultButton(
|
|
||||||
onPressed: state.errorMessage.isEmpty
|
|
||||||
? () {
|
|
||||||
final subSpacesBloc = context.read<SubSpaceBloc>();
|
|
||||||
final subSpaces = subSpacesBloc.state.subSpaces;
|
|
||||||
widget.onSave?.call(subSpaces);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
|
||||||
borderRadius: 10,
|
|
||||||
foregroundColor: state.errorMessage.isNotEmpty
|
|
||||||
? ColorsManager.whiteColorsWithOpacity
|
|
||||||
: ColorsManager.whiteColors,
|
|
||||||
child: const Text('OK'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
|
||||||
|
import '../../bloc/subspace_bloc.dart';
|
||||||
|
import '../../bloc/subspace_event.dart';
|
||||||
|
|
||||||
|
class OkCancelSubSpaceWidget extends StatelessWidget {
|
||||||
|
const OkCancelSubSpaceWidget({
|
||||||
|
super.key,
|
||||||
|
required this.subspaceNameController,
|
||||||
|
required this.onSave,
|
||||||
|
required this.errorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TextEditingController subspaceNameController;
|
||||||
|
final void Function(
|
||||||
|
List<SubspaceModel>?, List<UpdateSubspaceModel> updatedSubSpaces)? onSave;
|
||||||
|
final String errorMessage;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: CancelButton(
|
||||||
|
label: 'Cancel',
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: DefaultButton(
|
||||||
|
onPressed: errorMessage.isEmpty
|
||||||
|
? () async {
|
||||||
|
final trimmedValue = subspaceNameController.text.trim();
|
||||||
|
|
||||||
|
final subSpacesBloc = context.read<SubSpaceBloc>();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
|
subSpacesBloc.add(
|
||||||
|
AddSubSpace(
|
||||||
|
SubspaceModel(
|
||||||
|
subspaceName: trimmedValue,
|
||||||
|
disabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
subspaceNameController.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.delayed(const Duration(milliseconds: 10));
|
||||||
|
final subSpaces = subSpacesBloc.state.subSpaces;
|
||||||
|
|
||||||
|
onSave?.call(
|
||||||
|
subSpaces, subSpacesBloc.state.updatedSubSpaceModels);
|
||||||
|
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
|
borderRadius: 10,
|
||||||
|
foregroundColor: errorMessage.isNotEmpty
|
||||||
|
? ColorsManager.whiteColorsWithOpacity
|
||||||
|
: ColorsManager.whiteColors,
|
||||||
|
child: const Text('OK'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
import '../../../../../utils/color_manager.dart';
|
||||||
|
import '../../../all_spaces/model/subspace_model.dart';
|
||||||
|
import '../../../create_subspace_model/widgets/subspace_chip.dart';
|
||||||
|
import '../../../space_model/models/subspace_template_model.dart';
|
||||||
|
import '../../bloc/subspace_bloc.dart';
|
||||||
|
import '../../bloc/subspace_event.dart';
|
||||||
|
|
||||||
|
class TextFieldSubSpaceDialogWidget extends StatelessWidget {
|
||||||
|
const TextFieldSubSpaceDialogWidget({
|
||||||
|
super.key,
|
||||||
|
required TextEditingController subspaceNameController,
|
||||||
|
required this.subSpaces,
|
||||||
|
}) : _subspaceNameController = subspaceNameController;
|
||||||
|
|
||||||
|
final TextEditingController _subspaceNameController;
|
||||||
|
final List<SubspaceModel> subSpaces;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: context.screenWidth * 0.35,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager.boxColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
),
|
||||||
|
child: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 8,
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
|
children: [
|
||||||
|
...subSpaces.asMap().entries.map(
|
||||||
|
(entry) {
|
||||||
|
final index = entry.key;
|
||||||
|
final subSpace = entry.value;
|
||||||
|
|
||||||
|
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||||
|
|
||||||
|
final duplicateIndices = subSpaces
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.where((e) => e.value.subspaceName.toLowerCase() == lowerName)
|
||||||
|
.map((e) => e.key)
|
||||||
|
.toList();
|
||||||
|
final isDuplicate = duplicateIndices.length > 1 &&
|
||||||
|
duplicateIndices.indexOf(index) != 0;
|
||||||
|
return SubspaceChip(
|
||||||
|
subSpace: SubspaceTemplateModel(
|
||||||
|
subspaceName: entry.value.subspaceName,
|
||||||
|
disabled: entry.value.disabled,
|
||||||
|
),
|
||||||
|
isDuplicate: isDuplicate,
|
||||||
|
onDeleted: () => context.read<SubSpaceBloc>().add(
|
||||||
|
RemoveSubSpace(subSpace),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
child: TextField(
|
||||||
|
controller: _subspaceNameController,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
border: InputBorder.none,
|
||||||
|
hintText: subSpaces.isEmpty ? 'Please enter the name' : null,
|
||||||
|
hintStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
final trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
|
context.read<SubSpaceBloc>().add(
|
||||||
|
AddSubSpace(
|
||||||
|
SubspaceModel(
|
||||||
|
subspaceName: trimmedValue,
|
||||||
|
disabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_subspaceNameController.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: context.textTheme.bodyMedium,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -31,15 +31,19 @@ class CenterBodyWidget extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<CenterBodyBloc>().add(CommunityStructureSelectedEvent());
|
context
|
||||||
|
.read<CenterBodyBloc>()
|
||||||
|
.add(CommunityStructureSelectedEvent());
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Community Structure',
|
'Community Structure',
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
fontWeight: state is CommunityStructureState || state is CommunitySelectedState
|
fontWeight: state is CommunityStructureState ||
|
||||||
|
state is CommunitySelectedState
|
||||||
? FontWeight.bold
|
? FontWeight.bold
|
||||||
: FontWeight.normal,
|
: FontWeight.normal,
|
||||||
color: state is CommunityStructureState || state is CommunitySelectedState
|
color: state is CommunityStructureState ||
|
||||||
|
state is CommunitySelectedState
|
||||||
? Theme.of(context).textTheme.bodyLarge!.color
|
? Theme.of(context).textTheme.bodyLarge!.color
|
||||||
: Theme.of(context)
|
: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -50,26 +54,26 @@ class CenterBodyWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
GestureDetector(
|
// GestureDetector(
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
context.read<CenterBodyBloc>().add(SpaceModelSelectedEvent());
|
// context.read<CenterBodyBloc>().add(SpaceModelSelectedEvent());
|
||||||
},
|
// },
|
||||||
child: Text(
|
// child: Text(
|
||||||
'Space Model',
|
// 'Space Model',
|
||||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
// style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||||
fontWeight: state is SpaceModelState
|
// fontWeight: state is SpaceModelState
|
||||||
? FontWeight.bold
|
// ? FontWeight.bold
|
||||||
: FontWeight.normal,
|
// : FontWeight.normal,
|
||||||
color: state is SpaceModelState
|
// color: state is SpaceModelState
|
||||||
? Theme.of(context).textTheme.bodyLarge!.color
|
// ? Theme.of(context).textTheme.bodyLarge!.color
|
||||||
: Theme.of(context)
|
// : Theme.of(context)
|
||||||
.textTheme
|
// .textTheme
|
||||||
.bodyLarge!
|
// .bodyLarge!
|
||||||
.color!
|
// .color!
|
||||||
.withOpacity(0.5),
|
// .withOpacity(0.5),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -24,37 +24,22 @@ class AddDeviceTypeModelBloc
|
|||||||
|
|
||||||
if (currentState is AddDeviceModelLoaded) {
|
if (currentState is AddDeviceModelLoaded) {
|
||||||
final existingProduct = currentState.selectedProducts.firstWhere(
|
final existingProduct = currentState.selectedProducts.firstWhere(
|
||||||
(p) => p.productId == event.productId,
|
(p) => p.productId == event.selectedProduct.productId,
|
||||||
orElse: () => SelectedProduct(
|
orElse: () => event.selectedProduct,
|
||||||
productId: event.productId,
|
|
||||||
count: 0,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
List<SelectedProduct> updatedProducts;
|
List<SelectedProduct> updatedProducts;
|
||||||
|
|
||||||
if (event.count > 0) {
|
if (event.selectedProduct.count > 0) {
|
||||||
if (!currentState.selectedProducts.contains(existingProduct)) {
|
if (!currentState.selectedProducts.contains(existingProduct)) {
|
||||||
updatedProducts = [
|
updatedProducts = [
|
||||||
...currentState.selectedProducts,
|
...currentState.selectedProducts,
|
||||||
SelectedProduct(
|
event.selectedProduct,
|
||||||
productId: event.productId,
|
|
||||||
count: event.count,
|
|
||||||
productName: event.productName,
|
|
||||||
product: event.product,
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
updatedProducts = currentState.selectedProducts.map((p) {
|
updatedProducts = currentState.selectedProducts.map((p) {
|
||||||
if (p.productId == event.productId) {
|
if (p.productId == event.selectedProduct.productId) {
|
||||||
return SelectedProduct(
|
return event.selectedProduct;
|
||||||
productId: p.productId,
|
|
||||||
count: event.count,
|
|
||||||
productName: p.productName,
|
|
||||||
product: p.product,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}).toList();
|
}).toList();
|
||||||
@ -62,7 +47,7 @@ class AddDeviceTypeModelBloc
|
|||||||
} else {
|
} else {
|
||||||
// Remove the product if the count is 0
|
// Remove the product if the count is 0
|
||||||
updatedProducts = currentState.selectedProducts
|
updatedProducts = currentState.selectedProducts
|
||||||
.where((p) => p.productId != event.productId)
|
.where((p) => p.productId != event.selectedProduct.productId)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,20 +10,15 @@ abstract class AddDeviceTypeModelEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
||||||
final String productId;
|
final SelectedProduct selectedProduct;
|
||||||
final int count;
|
|
||||||
final String productName;
|
|
||||||
final ProductModel product;
|
|
||||||
|
|
||||||
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
UpdateProductCountEvent({required this.selectedProduct});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [productId, count];
|
List<Object> get props => [selectedProduct];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
||||||
final List<Tag> initialTags;
|
final List<Tag> initialTags;
|
||||||
final List<SelectedProduct> addedProducts;
|
final List<SelectedProduct> addedProducts;
|
||||||
|
@ -54,10 +54,10 @@ class DeviceTypeTileWidget extends StatelessWidget {
|
|||||||
onCountChanged: (newCount) {
|
onCountChanged: (newCount) {
|
||||||
context.read<AddDeviceTypeModelBloc>().add(
|
context.read<AddDeviceTypeModelBloc>().add(
|
||||||
UpdateProductCountEvent(
|
UpdateProductCountEvent(
|
||||||
productId: product.uuid,
|
selectedProduct: product.toSelectedProduct(
|
||||||
count: newCount,
|
newCount,
|
||||||
productName: product.catName,
|
),
|
||||||
product: product),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -22,6 +22,18 @@ class HTTPService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
client.interceptors.add(serviceLocator.get<HTTPInterceptor>());
|
client.interceptors.add(serviceLocator.get<HTTPInterceptor>());
|
||||||
|
// Add this interceptor for logging requests and responses
|
||||||
|
// client.interceptors.add(
|
||||||
|
// LogInterceptor(
|
||||||
|
// request: true,
|
||||||
|
// requestHeader: true,
|
||||||
|
// requestBody: true,
|
||||||
|
// responseHeader: false,
|
||||||
|
// responseBody: true,
|
||||||
|
// error: true,
|
||||||
|
// logPrint: (object) => print(object),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,12 +5,15 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_m
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_response_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
|
import '../pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||||
|
|
||||||
class CommunitySpaceManagementApi {
|
class CommunitySpaceManagementApi {
|
||||||
// Community Management APIs
|
// Community Management APIs
|
||||||
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
Future<List<CommunityModel>> fetchCommunities(String projectId,
|
||||||
@ -216,7 +219,6 @@ class CommunitySpaceManagementApi {
|
|||||||
{required String communityId,
|
{required String communityId,
|
||||||
required String name,
|
required String name,
|
||||||
String? parentId,
|
String? parentId,
|
||||||
String? direction,
|
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
String? spaceModelUuid,
|
String? spaceModelUuid,
|
||||||
@ -230,7 +232,6 @@ class CommunitySpaceManagementApi {
|
|||||||
'isPrivate': isPrivate,
|
'isPrivate': isPrivate,
|
||||||
'x': position.dx,
|
'x': position.dx,
|
||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
};
|
};
|
||||||
if (parentId != null) {
|
if (parentId != null) {
|
||||||
@ -265,11 +266,10 @@ class CommunitySpaceManagementApi {
|
|||||||
required String name,
|
required String name,
|
||||||
String? parentId,
|
String? parentId,
|
||||||
String? icon,
|
String? icon,
|
||||||
String? direction,
|
|
||||||
bool isPrivate = false,
|
bool isPrivate = false,
|
||||||
required Offset position,
|
required Offset position,
|
||||||
List<TagModelUpdate>? tags,
|
List<Tag>? tags,
|
||||||
List<UpdateSubspaceTemplateModel>? subspaces,
|
List<SubspaceModel>? subspaces,
|
||||||
String? spaceModelUuid,
|
String? spaceModelUuid,
|
||||||
required String projectId}) async {
|
required String projectId}) async {
|
||||||
try {
|
try {
|
||||||
@ -278,9 +278,8 @@ class CommunitySpaceManagementApi {
|
|||||||
'isPrivate': isPrivate,
|
'isPrivate': isPrivate,
|
||||||
'x': position.dx,
|
'x': position.dx,
|
||||||
'y': position.dy,
|
'y': position.dy,
|
||||||
'direction': direction,
|
|
||||||
'icon': icon,
|
'icon': icon,
|
||||||
'subspace': subspaces,
|
'subspaces': subspaces,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
'spaceModelUuid': spaceModelUuid,
|
'spaceModelUuid': spaceModelUuid,
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||||
@ -7,17 +9,23 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
|||||||
class SpaceModelManagementApi {
|
class SpaceModelManagementApi {
|
||||||
Future<List<SpaceTemplateModel>> listSpaceModels(
|
Future<List<SpaceTemplateModel>> listSpaceModels(
|
||||||
{required String projectId, int page = 1}) async {
|
{required String projectId, int page = 1}) async {
|
||||||
final response = await HTTPService().get(
|
try {
|
||||||
path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
// final response = await HTTPService().get(
|
||||||
queryParameters: {'page': page},
|
// path: ApiEndpoints.listSpaceModels.replaceAll('{projectId}', projectId),
|
||||||
expectedResponseModel: (json) {
|
// queryParameters: {'page': page},
|
||||||
List<dynamic> jsonData = json['data'];
|
// expectedResponseModel: (json) {
|
||||||
return jsonData.map((jsonItem) {
|
// List<dynamic> jsonData = json['data'];
|
||||||
return SpaceTemplateModel.fromJson(jsonItem);
|
// return jsonData.map((jsonItem) {
|
||||||
}).toList();
|
// return SpaceTemplateModel.fromJson(jsonItem);
|
||||||
},
|
// }).toList();
|
||||||
);
|
// },
|
||||||
return response;
|
// );
|
||||||
|
return [];
|
||||||
|
// response;
|
||||||
|
} catch (e) {
|
||||||
|
log(e.toString());
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> createSpaceModel(
|
Future<SpaceTemplateModel?> createSpaceModel(
|
||||||
@ -33,8 +41,8 @@ class SpaceModelManagementApi {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> updateSpaceModel(
|
Future<String?> updateSpaceModel(CreateSpaceTemplateBodyModel spaceModel,
|
||||||
CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid, String projectId) async {
|
String spaceModelUuid, String projectId) async {
|
||||||
final response = await HTTPService().put(
|
final response = await HTTPService().put(
|
||||||
path: ApiEndpoints.updateSpaceModel
|
path: ApiEndpoints.updateSpaceModel
|
||||||
.replaceAll('{projectId}', projectId)
|
.replaceAll('{projectId}', projectId)
|
||||||
@ -47,7 +55,8 @@ class SpaceModelManagementApi {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SpaceTemplateModel?> getSpaceModel(String spaceModelUuid, String projectId) async {
|
Future<SpaceTemplateModel?> getSpaceModel(
|
||||||
|
String spaceModelUuid, String projectId) async {
|
||||||
final response = await HTTPService().get(
|
final response = await HTTPService().get(
|
||||||
path: ApiEndpoints.getSpaceModel
|
path: ApiEndpoints.getSpaceModel
|
||||||
.replaceAll('{projectId}', projectId)
|
.replaceAll('{projectId}', projectId)
|
||||||
|
Reference in New Issue
Block a user