mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Merge pull request #72 from SyncrowIOT/bugfix/edit-subspace
Bugfix/edit-subspace
This commit is contained in:
@ -26,7 +26,7 @@ class EditChip extends StatelessWidget {
|
||||
child: Chip(
|
||||
label: Text(
|
||||
label,
|
||||
style: TextStyle(color: labelColor),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)
|
||||
),
|
||||
backgroundColor: backgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
@ -149,50 +149,53 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameController,
|
||||
onChanged: (value) {
|
||||
enteredName = value.trim();
|
||||
setState(() {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = false;
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: TextField(
|
||||
controller: nameController,
|
||||
onChanged: (value) {
|
||||
enteredName = value.trim();
|
||||
setState(() {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = false;
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (_isNameConflict(value)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = true;
|
||||
if (!isNameFieldInvalid) {
|
||||
if (_isNameConflict(value)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
style: const TextStyle(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: ColorsManager.boxColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: isNameFieldInvalid || isNameFieldExist
|
||||
? ColorsManager.red
|
||||
: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
});
|
||||
},
|
||||
style: const TextStyle(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(
|
||||
color: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -222,6 +225,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
const SizedBox(height: 10),
|
||||
selectedSpaceModel == null
|
||||
? TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () {
|
||||
_showLinkSpaceModelDialog(context);
|
||||
},
|
||||
@ -309,6 +315,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
subspaces == null
|
||||
? TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
overlayColor: ColorsManager.transparentColor,
|
||||
),
|
||||
onPressed: () async {
|
||||
@ -337,10 +344,21 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
if (subspaces != null)
|
||||
...subspaces!.map(
|
||||
(subspace) => SubspaceNameDisplayWidget(
|
||||
text: subspace.subspaceName,
|
||||
)),
|
||||
...subspaces!.map((subspace) =>
|
||||
SubspaceNameDisplayWidget(
|
||||
validateName: (updatedName) {
|
||||
return !subspaces!.any((s) =>
|
||||
s != subspace &&
|
||||
s.subspaceName == updatedName);
|
||||
},
|
||||
text: subspace.subspaceName,
|
||||
onNameChanged: (updatedName) {
|
||||
setState(() {
|
||||
subspace.subspaceName =
|
||||
updatedName;
|
||||
});
|
||||
},
|
||||
)),
|
||||
EditChip(
|
||||
onTap: () async {
|
||||
_showSubSpaceDialog(context, enteredName,
|
||||
|
@ -15,6 +15,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
|
||||
class AssignTagModelsDialog extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
@ -258,7 +259,6 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<TagModel>;
|
||||
final processedSubspaces = result['subspaces'];
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@ -270,8 +270,10 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
isCreate: false,
|
||||
initialSelectedProducts:
|
||||
addedProducts,
|
||||
initialSelectedProducts: TagHelper
|
||||
.createInitialSelectedProducts(
|
||||
processedTags,
|
||||
processedSubspaces),
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
|
@ -102,14 +102,19 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
name: value,
|
||||
allModels: otherSpaceModels ?? []));
|
||||
},
|
||||
style: const TextStyle(color: ColorsManager.blackColor),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: ColorsManager.textFieldGreyColor,
|
||||
hintText: 'Please enter the name',
|
||||
errorText: state.errorMessage,
|
||||
hintStyle: const TextStyle(
|
||||
color: ColorsManager.lightGrayColor),
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.lightGrayColor),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide.none,
|
||||
@ -123,7 +128,6 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SubspaceModelCreate(
|
||||
context,
|
||||
subspaces: state.space.subspaceModels ?? [],
|
||||
onSpaceModelUpdate: (updatedSubspaces) {
|
||||
context
|
||||
@ -160,7 +164,9 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
onPressed: (state.errorMessage == null)
|
||||
onPressed: ((state.errorMessage != null &&
|
||||
state.errorMessage != '') ||
|
||||
!isNameValid)
|
||||
? () {
|
||||
final updatedSpaceTemplate =
|
||||
updatedSpaceModel.copyWith(
|
||||
@ -238,8 +244,9 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
: null,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
borderRadius: 10,
|
||||
foregroundColor: (state.errorMessage != null &&
|
||||
state.errorMessage != '')
|
||||
foregroundColor: ((state.errorMessage != null &&
|
||||
state.errorMessage != '') ||
|
||||
!isNameValid)
|
||||
? ColorsManager.whiteColorsWithOpacity
|
||||
: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
@ -253,7 +260,10 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
} else if (state is CreateSpaceModelError) {
|
||||
return Text(
|
||||
'Error: ${state.message}',
|
||||
style: const TextStyle(color: ColorsManager.warningRed),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: ColorsManager.warningRed),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,20 +6,36 @@ import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SubspaceModelCreate extends StatelessWidget {
|
||||
class SubspaceModelCreate extends StatefulWidget {
|
||||
final List<SubspaceTemplateModel> subspaces;
|
||||
final void Function(List<SubspaceTemplateModel> newSubspaces)?
|
||||
onSpaceModelUpdate;
|
||||
|
||||
const SubspaceModelCreate(BuildContext context,
|
||||
{Key? key, required this.subspaces, this.onSpaceModelUpdate})
|
||||
: super(key: key);
|
||||
const SubspaceModelCreate({
|
||||
Key? key,
|
||||
required this.subspaces,
|
||||
this.onSpaceModelUpdate,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SubspaceModelCreateState createState() => _SubspaceModelCreateState();
|
||||
}
|
||||
|
||||
class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
||||
late List<SubspaceTemplateModel> _subspaces;
|
||||
String? errorSubspaceId;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_subspaces = List.from(widget.subspaces);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
return Container(
|
||||
child: subspaces.isEmpty
|
||||
child: _subspaces.isEmpty
|
||||
? TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
overlayColor: ColorsManager.transparentColor,
|
||||
@ -41,16 +57,33 @@ class SubspaceModelCreate extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: 3.0, // Border width
|
||||
width: 3.0,
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
...subspaces.map((subspace) => SubspaceNameDisplayWidget(
|
||||
text: subspace.subspaceName,
|
||||
)),
|
||||
..._subspaces.map((subspace) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SubspaceNameDisplayWidget(
|
||||
text: subspace.subspaceName,
|
||||
validateName: (updatedName) {
|
||||
return !_subspaces.any((s) =>
|
||||
s != subspace &&
|
||||
s.subspaceName == updatedName);
|
||||
},
|
||||
onNameChanged: (updatedName) {
|
||||
setState(() {
|
||||
subspace.subspaceName = updatedName;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
EditChip(
|
||||
onTap: () async {
|
||||
await _openDialog(context, 'Edit Sub-space');
|
||||
@ -71,9 +104,15 @@ class SubspaceModelCreate extends StatelessWidget {
|
||||
return CreateSubSpaceModelDialog(
|
||||
isEdit: true,
|
||||
dialogTitle: dialogTitle,
|
||||
existingSubSpaces: subspaces,
|
||||
existingSubSpaces: _subspaces,
|
||||
onUpdate: (subspaceModels) {
|
||||
onSpaceModelUpdate!(subspaceModels);
|
||||
setState(() {
|
||||
_subspaces = subspaceModels;
|
||||
errorSubspaceId = null;
|
||||
});
|
||||
if (widget.onSpaceModelUpdate != null) {
|
||||
widget.onSpaceModelUpdate!(subspaceModels);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -1,13 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SubspaceNameDisplayWidget extends StatelessWidget {
|
||||
class SubspaceNameDisplayWidget extends StatefulWidget {
|
||||
final String text;
|
||||
final TextStyle? textStyle;
|
||||
final Color backgroundColor;
|
||||
final Color borderColor;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final BorderRadiusGeometry borderRadius;
|
||||
final void Function(String updatedName) onNameChanged;
|
||||
final bool Function(String updatedName) validateName;
|
||||
|
||||
const SubspaceNameDisplayWidget({
|
||||
Key? key,
|
||||
@ -17,25 +19,112 @@ class SubspaceNameDisplayWidget extends StatelessWidget {
|
||||
this.borderColor = ColorsManager.transparentColor,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||
this.borderRadius = const BorderRadius.all(Radius.circular(10)),
|
||||
required this.onNameChanged,
|
||||
required this.validateName,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SubspaceNameDisplayWidgetState createState() =>
|
||||
_SubspaceNameDisplayWidgetState();
|
||||
}
|
||||
|
||||
class _SubspaceNameDisplayWidgetState extends State<SubspaceNameDisplayWidget> {
|
||||
bool isEditing = false;
|
||||
late TextEditingController _controller;
|
||||
late FocusNode _focusNode;
|
||||
late String previousName;
|
||||
String? errorText;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.text);
|
||||
_focusNode = FocusNode();
|
||||
previousName = widget.text;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _handleValidationAndSave() {
|
||||
final updatedName = _controller.text;
|
||||
if (widget.validateName(updatedName)) {
|
||||
setState(() {
|
||||
errorText = null;
|
||||
isEditing = false;
|
||||
previousName = updatedName;
|
||||
widget.onNameChanged(updatedName);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
errorText = 'Subspace name already exists.';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: padding,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: borderRadius,
|
||||
border: Border.all(color: borderColor),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: textStyle ??
|
||||
Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
isEditing = true;
|
||||
_focusNode.requestFocus();
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.backgroundColor,
|
||||
borderRadius: widget.borderRadius,
|
||||
border: Border.all(color: widget.borderColor),
|
||||
),
|
||||
child: isEditing
|
||||
? TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
style: widget.textStyle ??
|
||||
Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
),
|
||||
onSubmitted: (value) {
|
||||
_handleValidationAndSave();
|
||||
},
|
||||
)
|
||||
: Text(
|
||||
widget.text,
|
||||
style: widget.textStyle ??
|
||||
Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorText != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Text(
|
||||
errorText!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: ColorsManager.warningRed),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -73,9 +73,12 @@ class TagChipDisplay extends StatelessWidget {
|
||||
),
|
||||
label: Text(
|
||||
'x${entry.value}', // Show count
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
Reference in New Issue
Block a user