Compare commits

..

18 Commits

Author SHA1 Message Date
9044645f95 remove screenWidth parameter from TagChipDisplay and use context.screenWidth instead. 2025-04-15 15:45:47 +03:00
7699453e6d moved styling of _buildChip up, and removed unnecessary SizedBox. 2025-04-15 15:43:50 +03:00
d1a21be983 removed else from TagChipDisplay.build 2025-04-15 15:35:24 +03:00
db8e5a4aa6 Refactor TagChipDisplay._groupedTags to enhance readabaility. 2025-04-15 15:32:31 +03:00
fa5bb350c3 refactor: replace spaceNameController with spaceName in TagChipDisplay. 2025-04-15 15:29:25 +03:00
920827d763 Removed unnecessary SizedBox from TagChipDisplay. 2025-04-15 15:28:12 +03:00
d3902d622e Moved constructor to be the first element in TagChipDisplay. 2025-04-15 15:27:45 +03:00
a4432656ab refactor: extract EditChip into a private method for improved readability 2025-04-15 15:27:04 +03:00
90e0d2f52b Extracted Chip into a private method. 2025-04-15 15:24:36 +03:00
08e5e17910 Extracted Add Devices button into a private method. 2025-04-15 15:23:36 +03:00
f57348e5cd converted to using expressions wherever possible in TagChipDisplay. 2025-04-15 15:13:47 +03:00
be168aed93 refactor: simplify tag checking logic to enhance readability. 2025-04-15 15:08:30 +03:00
a66784473f Replaced conditional with an if statement in TagChipDisplay. 2025-04-15 15:06:01 +03:00
c0a963ded5 refactor: use context extension for text theme. 2025-04-15 15:05:31 +03:00
7945cefe53 added trailing commas wherever necessary. 2025-04-15 15:05:06 +03:00
7d0e50fb1d removed unnecessary comments. 2025-04-15 15:03:27 +03:00
117f6190dd removed unnecessary BuildContext from TagChipDisplay constructor, sorted its properies, and converted to using super.key. 2025-04-15 15:02:58 +03:00
748c67fd8b SP-1333 2025-04-15 14:52:20 +03:00
12 changed files with 378 additions and 482 deletions

View File

@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget {
final int? dialogWidth;
const DialogFooter({
super.key,
Key? key,
required this.onCancel,
required this.onConfirm,
required this.isConfirmEnabled,
this.dialogWidth,
});
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -28,19 +28,21 @@ class DialogFooter extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildFooterButton(
context: context,
text: 'Cancel',
onTap: onCancel,
Expanded(
child: _buildFooterButton(
context,
'Cancel',
onCancel,
),
),
if (isConfirmEnabled) ...[
Container(width: 1, height: 50, color: ColorsManager.greyColor),
_buildFooterButton(
context: context,
text: 'Confirm',
onTap: onConfirm,
textColor:
isConfirmEnabled ? ColorsManager.primaryColorWithOpacity : Colors.red,
Expanded(
child: _buildFooterButton(
context,
'Confirm',
onConfirm,
),
),
],
],
@ -48,24 +50,24 @@ class DialogFooter extends StatelessWidget {
);
}
Widget _buildFooterButton({
required BuildContext context,
required String text,
required VoidCallback? onTap,
Color? textColor,
}) {
return Expanded(
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: ColorsManager.primaryColorWithOpacity,
disabledForegroundColor: ColorsManager.primaryColor,
),
onPressed: onTap,
child: Text(
text,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: textColor ?? ColorsManager.textGray,
),
Widget _buildFooterButton(
BuildContext context,
String text,
VoidCallback? onTap,
) {
return GestureDetector(
onTap: onTap,
child: SizedBox(
height: 50,
child: Center(
child: Text(
text,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: text == 'Confirm'
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
),
),
),
);

View File

@ -2,8 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
@ -109,11 +107,6 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
selectedSpaceUuid: widget.selectedSpace?.uuid ??
widget.selectedCommunity?.uuid ??
'',
onCreateCommunity: (name, description) {
context.read<SpaceManagementBloc>().add(
CreateCommunityEvent(name, description, context),
);
},
),
CommunityStructureArea(
selectedCommunity: widget.selectedCommunity,

View File

@ -1,15 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class SidebarAddCommunityButton extends StatelessWidget {
const SidebarAddCommunityButton({
required this.onTap,
required this.existingCommunityNames,
super.key,
});
final void Function() onTap;
final List<String> existingCommunityNames;
@override
Widget build(BuildContext context) {
@ -26,9 +30,22 @@ class SidebarAddCommunityButton extends StatelessWidget {
),
),
),
onPressed: onTap,
onPressed: () => _showCreateCommunityDialog(context),
icon: SvgPicture.asset(Assets.addIcon),
),
);
}
void _showCreateCommunityDialog(BuildContext context) => showDialog<void>(
context: context,
builder: (context) => CreateCommunityDialog(
isEditMode: false,
existingCommunityNames: existingCommunityNames,
onCreateCommunity: (name, description) {
context.read<SpaceManagementBloc>().add(
CreateCommunityEvent(name, description, context),
);
},
),
);
}

View File

@ -5,12 +5,9 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart';
class SidebarHeader extends StatelessWidget {
const SidebarHeader({
required this.onAddCommunity,
super.key,
});
const SidebarHeader({required this.existingCommunityNames, super.key});
final void Function() onAddCommunity;
final List<String> existingCommunityNames;
@override
Widget build(BuildContext context) {
@ -26,9 +23,7 @@ class SidebarHeader extends StatelessWidget {
color: ColorsManager.blackColor,
),
),
SidebarAddCommunityButton(
onTap: onAddCommunity,
),
SidebarAddCommunityButton(existingCommunityNames: existingCommunityNames),
],
),
);

View File

@ -8,7 +8,6 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_header.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
import 'package:syncrow_web/utils/style.dart';
@ -16,11 +15,9 @@ import 'package:syncrow_web/utils/style.dart';
class SidebarWidget extends StatefulWidget {
final List<CommunityModel> communities;
final String? selectedSpaceUuid;
final void Function(String name, String description) onCreateCommunity;
const SidebarWidget({
required this.communities,
required this.onCreateCommunity,
this.selectedSpaceUuid,
super.key,
});
@ -97,7 +94,10 @@ class _SidebarWidgetState extends State<SidebarWidget> {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SidebarHeader(onAddCommunity: _onAddCommunity),
SidebarHeader(
existingCommunityNames:
widget.communities.map((community) => community.name).toList(),
),
CustomSearchBar(
onSearchChanged: (query) => setState(() => _searchQuery = query),
),
@ -179,26 +179,4 @@ class _SidebarWidgetState extends State<SidebarWidget> {
),
);
}
void _onAddCommunity() => _selectedId?.isNotEmpty ?? true
? _clearSelection()
: _showCreateCommunityDialog();
void _clearSelection() {
setState(() => _selectedId = '');
context.read<SpaceManagementBloc>().add(
NewCommunityEvent(communities: widget.communities),
);
}
void _showCreateCommunityDialog() {
showDialog<void>(
context: context,
builder: (context) => CreateCommunityDialog(
isEditMode: false,
existingCommunityNames: widget.communities.map((e) => e.name).toList(),
onCreateCommunity: widget.onCreateCommunity,
),
);
}
}

View File

@ -1,13 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_state.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_chips_box.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/create_subspace_model_footer_buttons.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class CreateSubSpaceModelDialog extends StatelessWidget {
final bool isEdit;
@ -15,67 +14,211 @@ class CreateSubSpaceModelDialog extends StatelessWidget {
final List<SubspaceTemplateModel>? existingSubSpaces;
final void Function(List<SubspaceTemplateModel> newSubspaces)? onUpdate;
const CreateSubSpaceModelDialog({
required this.isEdit,
required this.dialogTitle,
this.existingSubSpaces,
this.onUpdate,
super.key,
});
const CreateSubSpaceModelDialog(
{Key? key,
required this.isEdit,
required this.dialogTitle,
this.existingSubSpaces,
this.onUpdate})
: super(key: key);
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
final textController = TextEditingController();
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: BlocProvider(
create: (context) {
create: (_) {
final bloc = SubSpaceModelBloc();
if (existingSubSpaces != null) {
for (final subSpace in existingSubSpaces ?? []) {
for (var subSpace in existingSubSpaces!) {
bloc.add(AddSubSpaceModel(subSpace));
}
}
return bloc;
},
child: BlocBuilder<SubSpaceModelBloc, SubSpaceModelState>(
builder: (context, state) => Container(
color: ColorsManager.whiteColors,
width: screenWidth * 0.3,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
dialogTitle,
style: context.textTheme.headlineLarge?.copyWith(
color: ColorsManager.blackColor,
),
),
const SizedBox(height: 16),
CreateSubspaceModelChipsBox(subSpaces: state.subSpaces),
if (state.errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Text(
state.errorMessage,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.red,
),
builder: (context, state) {
return Container(
color: ColorsManager.whiteColors,
child: SizedBox(
width: screenWidth * 0.3,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
dialogTitle,
style: Theme.of(context)
.textTheme
.headlineLarge
?.copyWith(color: ColorsManager.blackColor),
),
const SizedBox(height: 16),
Container(
width: screenWidth * 0.35,
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: [
...state.subSpaces.asMap().entries.map(
(entry) {
final index = entry.key;
final subSpace = entry.value;
final lowerName =
subSpace.subspaceName.toLowerCase();
final duplicateIndices = state.subSpaces
.asMap()
.entries
.where((e) =>
e.value.subspaceName.toLowerCase() ==
lowerName)
.map((e) => e.key)
.toList();
final isDuplicate =
duplicateIndices.length > 1 &&
duplicateIndices.indexOf(index) != 0;
return Chip(
label: Text(subSpace.subspaceName,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: ColorsManager.spaceColor,
)),
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: isDuplicate
? ColorsManager.red
: 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: () => context
.read<SubSpaceModelBloc>()
.add(RemoveSubSpaceModel(subSpace)),
);
},
),
SizedBox(
width: 200,
child: TextField(
controller: textController,
decoration: InputDecoration(
border: InputBorder.none,
hintText: state.subSpaces.isEmpty
? 'Please enter the name'
: null,
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager
.lightGrayColor)),
onSubmitted: (value) {
if (value.trim().isNotEmpty) {
context.read<SubSpaceModelBloc>().add(
AddSubSpaceModel(
SubspaceTemplateModel(
subspaceName: value.trim(),
disabled: false)));
textController.clear();
}
},
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.blackColor)),
),
],
),
),
if (state.errorMessage.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Text(state.errorMessage,
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(
color: ColorsManager.red,
)),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: CancelButton(
label: 'Cancel',
onPressed: () async {
Navigator.of(context).pop();
},
),
),
const SizedBox(width: 10),
Expanded(
child: DefaultButton(
onPressed: (state.errorMessage.isNotEmpty)
? null
: () async {
final subSpaces = context
.read<SubSpaceModelBloc>()
.state
.subSpaces;
Navigator.of(context).pop();
if (onUpdate != null) {
onUpdate!(subSpaces);
}
},
backgroundColor: ColorsManager.secondaryColor,
borderRadius: 10,
foregroundColor: state.errorMessage.isNotEmpty
? ColorsManager.whiteColorsWithOpacity
: ColorsManager.whiteColors,
child: const Text('OK'),
),
),
],
),
],
),
),
const SizedBox(height: 16),
CreateSubspaceModelFooterButtons(
onUpdate: onUpdate,
errorMessage: state.errorMessage,
),
],
),
),
));
},
),
),
);

View File

@ -1,64 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspace_chip.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/widgets/subspaces_textfield.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CreateSubspaceModelChipsBox extends StatelessWidget {
const CreateSubspaceModelChipsBox({
required this.subSpaces,
super.key,
});
final List<SubspaceTemplateModel> subSpaces;
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Container(
width: 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: subSpace,
isDuplicate: isDuplicate,
);
},
),
SubspacesTextfield(
hintText: subSpaces.isEmpty ? 'Please enter the name' : null,
),
],
),
);
}
}

View File

@ -1,53 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CreateSubspaceModelFooterButtons extends StatelessWidget {
const CreateSubspaceModelFooterButtons({
required this.onUpdate,
required this.errorMessage,
super.key,
});
final void Function(List<SubspaceTemplateModel> newSubspaces)? onUpdate;
final String errorMessage;
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: CancelButton(
label: 'Cancel',
onPressed: () => Navigator.of(context).pop(),
),
),
const SizedBox(width: 10),
Expanded(
child: DefaultButton(
onPressed: errorMessage.isEmpty
? () {
Navigator.of(context).pop();
if (onUpdate != null) {
final subSpaces =
context.read<SubSpaceModelBloc>().state.subSpaces;
onUpdate!(subSpaces);
}
}
: null,
backgroundColor: ColorsManager.secondaryColor,
borderRadius: 10,
foregroundColor: errorMessage.isNotEmpty
? ColorsManager.whiteColorsWithOpacity
: ColorsManager.whiteColors,
child: const Text('OK'),
),
),
],
);
}
}

View File

@ -1,58 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SubspaceChip extends StatelessWidget {
const SubspaceChip({
required this.subSpace,
required this.isDuplicate,
super.key,
});
final SubspaceTemplateModel subSpace;
final bool isDuplicate;
@override
Widget build(BuildContext context) {
return Chip(
label: Text(
subSpace.subspaceName,
style: context.textTheme.bodySmall?.copyWith(
color: isDuplicate ? ColorsManager.red : ColorsManager.spaceColor,
),
),
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(
color: isDuplicate ? ColorsManager.red : ColorsManager.transparentColor,
width: 0,
),
),
deleteIcon: Container(
padding: const EdgeInsetsDirectional.all(1),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.lightGrayColor,
width: 1.5,
),
),
child: const FittedBox(
fit: BoxFit.scaleDown,
child: Icon(
Icons.close,
color: ColorsManager.lightGrayColor,
),
),
),
onDeleted: () => context.read<SubSpaceModelBloc>().add(
RemoveSubSpaceModel(subSpace),
),
);
}
}

View File

@ -1,68 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/bloc/subspace_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SubspacesTextfield extends StatefulWidget {
const SubspacesTextfield({
required this.hintText,
super.key,
});
final String? hintText;
@override
State<SubspacesTextfield> createState() => _SubspacesTextfieldState();
}
class _SubspacesTextfieldState extends State<SubspacesTextfield> {
late final TextEditingController _controller;
@override
void initState() {
_controller = TextEditingController();
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: 100,
child: TextField(
controller: _controller,
decoration: InputDecoration(
border: InputBorder.none,
hintText: widget.hintText,
hintStyle: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGrayColor,
),
),
onSubmitted: (value) {
final trimmedValue = value.trim();
if (trimmedValue.isNotEmpty) {
context.read<SubSpaceModelBloc>().add(
AddSubSpaceModel(
SubspaceTemplateModel(
subspaceName: trimmedValue,
disabled: false,
),
),
);
_controller.clear();
}
},
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.blackColor,
),
),
);
}
}

View File

@ -145,13 +145,11 @@ class CreateSpaceModelDialog extends StatelessWidget {
),
const SizedBox(height: 10),
TagChipDisplay(
context,
screenWidth: screenWidth,
spaceModel: updatedSpaceModel,
products: products,
subspaces: subspaces,
allTags: allTags,
spaceNameController: spaceNameController,
spaceName: spaceNameController.text,
pageContext: pageContext,
otherSpaceModels: otherSpaceModels,
allSpaceModels: allSpaceModels,

View File

@ -10,139 +10,152 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class TagChipDisplay extends StatelessWidget {
final double screenWidth;
const TagChipDisplay({
required this.spaceModel,
required this.products,
required this.subspaces,
required this.allTags,
required this.spaceName,
required this.projectTags,
this.pageContext,
this.otherSpaceModels,
this.allSpaceModels,
super.key,
});
final SpaceTemplateModel? spaceModel;
final List<ProductModel>? products;
final List<SubspaceTemplateModel>? subspaces;
final List<String>? allTags;
final TextEditingController spaceNameController;
final String spaceName;
final BuildContext? pageContext;
final List<String>? otherSpaceModels;
final List<SpaceTemplateModel>? allSpaceModels;
final List<Tag> projectTags;
const TagChipDisplay(BuildContext context,
{Key? key,
required this.screenWidth,
required this.spaceModel,
required this.products,
required this.subspaces,
required this.allTags,
required this.spaceNameController,
this.pageContext,
this.otherSpaceModels,
this.allSpaceModels,
required this.projectTags})
: super(key: key);
Map<ProductModel, int> get _groupedTags {
final spaceTags = spaceModel?.tags ?? <Tag>[];
final subspaces = spaceModel?.subspaceModels ?? [];
final subspaceTags = subspaces.expand((e) => e.tags ?? <Tag>[]).toList();
return TagHelper.groupTags([...spaceTags, ...subspaceTags]);
}
@override
Widget build(BuildContext context) {
return (spaceModel?.tags?.isNotEmpty == true ||
spaceModel?.subspaceModels?.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([
...?spaceModel?.tags,
...?spaceModel?.subspaceModels?.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 {
// Use the Navigator's context for showDialog
Navigator.of(context).pop();
final hasTags = spaceModel?.tags?.isNotEmpty ?? false;
final hasSubspaceTags =
spaceModel?.subspaceModels?.any((e) => e.tags?.isNotEmpty ?? false) ?? false;
await showDialog<bool>(
barrierDismissible: false,
context: context,
builder: (context) => AssignTagModelsDialog(
products: products,
allSpaceModels: allSpaceModels,
subspaces: subspaces,
pageContext: pageContext,
allTags: allTags,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
initialTags: TagHelper.generateInitialTags(
subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []),
title: 'Edit Device',
addedProducts: TagHelper.createInitialSelectedProducts(
spaceModel?.tags ?? [], subspaces),
spaceName: spaceModel?.modelName ?? '',
projectTags: projectTags,
));
})
],
),
),
)
: TextButton(
onPressed: () async {
Navigator.of(context).pop();
if (hasTags || hasSubspaceTags) {
return Container(
width: context.screenWidth * 0.25,
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: ColorsManager.textFieldGreyColor,
borderRadius: BorderRadius.circular(15),
border: Border.all(
color: ColorsManager.textFieldGreyColor,
width: 3,
),
),
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
..._groupedTags.entries.map((entry) => _buildChip(context, entry)),
_buildEditChip(context),
],
),
);
}
await showDialog<bool>(
barrierDismissible: false,
context: context,
builder: (context) => AddDeviceTypeModelWidget(
products: products,
subspaces: subspaces,
allTags: allTags,
spaceName: spaceNameController.text,
pageContext: pageContext,
isCreate: true,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
projectTags: projectTags,
),
);
},
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
child: const ButtonContentWidget(
icon: Icons.add,
label: 'Add Devices',
),
);
return _buildAddDevicesButton(context);
}
Widget _buildEditChip(BuildContext context) {
return EditChip(
onTap: () => showDialog<void>(
context: context,
builder: (context) => AssignTagModelsDialog(
products: products,
allSpaceModels: allSpaceModels,
subspaces: subspaces,
pageContext: pageContext,
allTags: allTags,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
initialTags: TagHelper.generateInitialTags(
subspaces: subspaces,
spaceTagModels: spaceModel?.tags ?? [],
),
title: 'Edit Device',
addedProducts: TagHelper.createInitialSelectedProducts(
spaceModel?.tags ?? [],
subspaces,
),
spaceName: spaceModel?.modelName ?? '',
projectTags: projectTags,
),
),
);
}
Widget _buildChip(
BuildContext context,
MapEntry<ProductModel, int> entry,
) {
return Chip(
backgroundColor: ColorsManager.whiteColors,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(
color: ColorsManager.spaceColor,
),
),
avatar: SvgPicture.asset(
entry.key.icon ?? Assets.gateway,
fit: BoxFit.contain,
height: 24,
width: 24,
),
label: Text(
'${entry.value}',
style: context.textTheme.bodySmall!.copyWith(
color: ColorsManager.spaceColor,
),
),
);
}
Widget _buildAddDevicesButton(BuildContext context) {
return TextButton(
onPressed: () => showDialog<void>(
context: context,
builder: (context) => AddDeviceTypeModelWidget(
products: products,
subspaces: subspaces,
allTags: allTags,
spaceName: spaceName,
pageContext: pageContext,
isCreate: true,
spaceModel: spaceModel,
otherSpaceModels: otherSpaceModels,
projectTags: projectTags,
),
),
style: TextButton.styleFrom(
padding: EdgeInsets.zero,
),
child: const ButtonContentWidget(
icon: Icons.add,
label: 'Add Devices',
),
);
}
}