mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-24 23:42:28 +00:00
Sp 1722 duplicate space dialog enhancement (#370)
<!-- 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-1722](https://syncrow.atlassian.net/browse/SP-1722) ## Description Implemented naming validation, that disallows for any name duplication. Adds proper name suffix to the duplicated space's name, if there is any match with the siblings. Enhanced the dialog's design to match the design language of the application. ## Type of Change <!--- Put an `x` in all the boxes that apply: --> - [ x ] ✨ New feature (non-breaking change which adds functionality) - [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) - [ x ] ❌ Breaking change (fix or feature that would cause existing functionality to change) - [ ] 🧹 Code refactor - [ ] ✅ Build configuration change - [ ] 📝 Documentation - [ ] 🗑️ Chore [SP-1722]: https://syncrow.atlassian.net/browse/SP-1722?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This commit is contained in:
@ -68,4 +68,20 @@ abstract final class SpacesRecursiveHelper {
|
|||||||
return space;
|
return space;
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SpaceModel? findParent(
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
String targetUuid,
|
||||||
|
) {
|
||||||
|
for (final space in spaces) {
|
||||||
|
if (space.children.any((child) => child.uuid == targetUuid)) {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
final parent = findParent(space.children, targetUuid);
|
||||||
|
if (parent != null) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +49,8 @@ class CommunityStructureHeaderActionButtonsComposer extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (_) => DuplicateSpaceDialog(
|
builder: (_) => DuplicateSpaceDialog(
|
||||||
initialName: space.spaceName,
|
initialName: space.spaceName,
|
||||||
selectedSpaceUuid: space.uuid,
|
selectedSpace: space,
|
||||||
selectedCommunityUuid: selectedCommunity.uuid,
|
selectedCommunity: selectedCommunity,
|
||||||
onSuccess: (spaces) {
|
onSuccess: (spaces) {
|
||||||
final updatedCommunity = selectedCommunity.copyWith(
|
final updatedCommunity = selectedCommunity.copyWith(
|
||||||
spaces: spaces,
|
spaces: spaces,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/common/widgets/app_loading_indicator.dart';
|
import 'package:syncrow_web/common/widgets/app_loading_indicator.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/space_model.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/data/services/remote_duplicate_space_service.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/data/services/remote_duplicate_space_service.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart';
|
||||||
@ -12,15 +13,15 @@ class DuplicateSpaceDialog extends StatelessWidget {
|
|||||||
const DuplicateSpaceDialog({
|
const DuplicateSpaceDialog({
|
||||||
required this.initialName,
|
required this.initialName,
|
||||||
required this.onSuccess,
|
required this.onSuccess,
|
||||||
required this.selectedSpaceUuid,
|
required this.selectedSpace,
|
||||||
required this.selectedCommunityUuid,
|
required this.selectedCommunity,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String initialName;
|
final String initialName;
|
||||||
final void Function(List<SpaceModel> spaces) onSuccess;
|
final void Function(List<SpaceModel> spaces) onSuccess;
|
||||||
final String selectedSpaceUuid;
|
final SpaceModel selectedSpace;
|
||||||
final String selectedCommunityUuid;
|
final CommunityModel selectedCommunity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -31,9 +32,10 @@ class DuplicateSpaceDialog extends StatelessWidget {
|
|||||||
child: BlocListener<DuplicateSpaceBloc, DuplicateSpaceState>(
|
child: BlocListener<DuplicateSpaceBloc, DuplicateSpaceState>(
|
||||||
listener: _listener,
|
listener: _listener,
|
||||||
child: DuplicateSpaceDialogForm(
|
child: DuplicateSpaceDialogForm(
|
||||||
initialName: initialName,
|
initialName: _getInitialName(),
|
||||||
selectedSpaceUuid: selectedSpaceUuid,
|
selectedSpaceUuid: selectedSpace.uuid,
|
||||||
selectedCommunityUuid: selectedCommunityUuid,
|
selectedCommunityUuid: selectedCommunity.uuid,
|
||||||
|
siblingNames: _getSiblingNames(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -66,4 +68,69 @@ class DuplicateSpaceDialog extends StatelessWidget {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<SpaceModel> _findSiblings(
|
||||||
|
List<SpaceModel> spaces,
|
||||||
|
String targetUuid,
|
||||||
|
) {
|
||||||
|
final parent = _findParent(spaces, targetUuid);
|
||||||
|
|
||||||
|
if (parent != null) {
|
||||||
|
return parent.children.where((s) => s.uuid != targetUuid).toList();
|
||||||
|
} else {
|
||||||
|
if (spaces.any((s) => s.uuid == targetUuid)) {
|
||||||
|
return spaces.where((s) => s.uuid != targetUuid).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
SpaceModel? _findParent(List<SpaceModel> allSpaces, String childUuid) {
|
||||||
|
for (final space in allSpaces) {
|
||||||
|
if (space.children.any((child) => child.uuid == childUuid)) {
|
||||||
|
return space;
|
||||||
|
}
|
||||||
|
final parent = _findParent(space.children, childUuid);
|
||||||
|
if (parent != null) {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _getSiblingNames() {
|
||||||
|
final siblings = _findSiblings(selectedCommunity.spaces, selectedSpace.uuid);
|
||||||
|
final names = siblings.map((s) => s.spaceName).toList();
|
||||||
|
names.add(initialName);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getInitialName() {
|
||||||
|
final allRelevantNames = _getSiblingNames();
|
||||||
|
final nameRegex = RegExp(r'^(.*?) ?\((\d+)\)$');
|
||||||
|
|
||||||
|
final baseNameMatch = nameRegex.firstMatch(initialName);
|
||||||
|
final baseName = baseNameMatch?.group(1)?.trim() ?? initialName;
|
||||||
|
|
||||||
|
var maxSuffix = 0;
|
||||||
|
|
||||||
|
for (final name in allRelevantNames) {
|
||||||
|
if (name == baseName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final match = nameRegex.firstMatch(name);
|
||||||
|
if (match != null) {
|
||||||
|
final currentBaseName = match.group(1)!.trim();
|
||||||
|
if (currentBaseName == baseName) {
|
||||||
|
final suffix = int.parse(match.group(2)!);
|
||||||
|
if (suffix > maxSuffix) {
|
||||||
|
maxSuffix = suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '$baseName(${maxSuffix + 1})';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,22 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/domain/params/duplicate_space_param.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/domain/params/duplicate_space_param.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/bloc/duplicate_space_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_text_field.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/duplicate_space/presentation/widgets/duplicate_space_text_field.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class DuplicateSpaceDialogForm extends StatefulWidget {
|
class DuplicateSpaceDialogForm extends StatefulWidget {
|
||||||
const DuplicateSpaceDialogForm({
|
const DuplicateSpaceDialogForm({
|
||||||
required this.initialName,
|
required this.initialName,
|
||||||
required this.selectedSpaceUuid,
|
required this.selectedSpaceUuid,
|
||||||
required this.selectedCommunityUuid,
|
required this.selectedCommunityUuid,
|
||||||
|
required this.siblingNames,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String initialName;
|
final String initialName;
|
||||||
final String selectedSpaceUuid;
|
final String selectedSpaceUuid;
|
||||||
final String selectedCommunityUuid;
|
final String selectedCommunityUuid;
|
||||||
|
final List<String> siblingNames;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DuplicateSpaceDialogForm> createState() => _DuplicateSpaceDialogFormState();
|
State<DuplicateSpaceDialogForm> createState() => _DuplicateSpaceDialogFormState();
|
||||||
@ -22,18 +26,33 @@ class DuplicateSpaceDialogForm extends StatefulWidget {
|
|||||||
|
|
||||||
class _DuplicateSpaceDialogFormState extends State<DuplicateSpaceDialogForm> {
|
class _DuplicateSpaceDialogFormState extends State<DuplicateSpaceDialogForm> {
|
||||||
late final TextEditingController _nameController;
|
late final TextEditingController _nameController;
|
||||||
bool _isNameValid = true;
|
String? _errorText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_nameController = TextEditingController(text: '${widget.initialName}(1)');
|
_nameController = TextEditingController(
|
||||||
|
text: widget.initialName,
|
||||||
|
);
|
||||||
_nameController.addListener(_validateName);
|
_nameController.addListener(_validateName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _validateName() => setState(
|
void _validateName() {
|
||||||
() => _isNameValid = _nameController.text.trim() != widget.initialName,
|
final name = _nameController.text.trim();
|
||||||
);
|
if (name.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_errorText = 'Name cannot be empty';
|
||||||
|
});
|
||||||
|
} else if (widget.siblingNames.contains(name)) {
|
||||||
|
setState(() {
|
||||||
|
_errorText = 'Name must be unique';
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_errorText = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@ -44,27 +63,32 @@ class _DuplicateSpaceDialogFormState extends State<DuplicateSpaceDialogForm> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const SelectableText('Duplicate Space'),
|
title: const SelectableText(
|
||||||
|
'Duplicate Space',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 16,
|
spacing: 16,
|
||||||
children: [
|
children: [
|
||||||
const SelectableText('Enter a new name for the duplicated space:'),
|
const SelectableText('Enter a new name for the duplicated space:'),
|
||||||
DuplicateSpaceTextField(
|
DuplicateSpaceTextField(
|
||||||
nameController: _nameController,
|
nameController: _nameController,
|
||||||
isNameValid: _isNameValid,
|
errorText: _errorText,
|
||||||
initialName: widget.initialName,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
SpaceDetailsActionButtons(
|
||||||
onPressed: Navigator.of(context).pop,
|
spacerFlex: 2,
|
||||||
child: const Text('Cancel'),
|
onSave: _errorText == null ? () => _submit(context) : null,
|
||||||
),
|
onCancel: Navigator.of(context).pop,
|
||||||
TextButton(
|
saveButtonLabel: 'Duplicate',
|
||||||
onPressed: _isNameValid ? () => _submit(context) : null,
|
|
||||||
child: const Text('Duplicate'),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -5,16 +5,12 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|||||||
class DuplicateSpaceTextField extends StatelessWidget {
|
class DuplicateSpaceTextField extends StatelessWidget {
|
||||||
const DuplicateSpaceTextField({
|
const DuplicateSpaceTextField({
|
||||||
required this.nameController,
|
required this.nameController,
|
||||||
required this.isNameValid,
|
required this.errorText,
|
||||||
required this.initialName,
|
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final TextEditingController nameController;
|
final TextEditingController nameController;
|
||||||
final bool isNameValid;
|
final String? errorText;
|
||||||
final String initialName;
|
|
||||||
|
|
||||||
String get _errorText => 'Name must be different from "$initialName"';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -24,9 +20,10 @@ class DuplicateSpaceTextField extends StatelessWidget {
|
|||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
label: const Text('Space Name'),
|
filled: true,
|
||||||
border: _border(),
|
fillColor: ColorsManager.boxColor,
|
||||||
enabledBorder: _border(),
|
border: _border(ColorsManager.transparentColor),
|
||||||
|
enabledBorder: _border(ColorsManager.transparentColor),
|
||||||
focusedBorder: _border(ColorsManager.primaryColor),
|
focusedBorder: _border(ColorsManager.primaryColor),
|
||||||
errorBorder: _border(context.theme.colorScheme.error),
|
errorBorder: _border(context.theme.colorScheme.error),
|
||||||
focusedErrorBorder: _border(context.theme.colorScheme.error),
|
focusedErrorBorder: _border(context.theme.colorScheme.error),
|
||||||
@ -34,14 +31,14 @@ class DuplicateSpaceTextField extends StatelessWidget {
|
|||||||
color: context.theme.colorScheme.error,
|
color: context.theme.colorScheme.error,
|
||||||
fontSize: 8,
|
fontSize: 8,
|
||||||
),
|
),
|
||||||
errorText: isNameValid ? null : _errorText,
|
errorText: errorText,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutlineInputBorder _border([Color? color]) {
|
OutlineInputBorder _border([Color? color]) {
|
||||||
return OutlineInputBorder(
|
return OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(8),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: color ?? ColorsManager.blackColor,
|
color: color ?? ColorsManager.blackColor,
|
||||||
width: 0.5,
|
width: 0.5,
|
||||||
|
@ -10,7 +10,7 @@ import 'package:syncrow_web/pages/space_management_v2/modules/update_space/prese
|
|||||||
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';
|
||||||
|
|
||||||
class SpaceDetailsForm extends StatelessWidget {
|
class SpaceDetailsForm extends StatefulWidget {
|
||||||
const SpaceDetailsForm({
|
const SpaceDetailsForm({
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.space,
|
required this.space,
|
||||||
@ -22,24 +22,52 @@ class SpaceDetailsForm extends StatelessWidget {
|
|||||||
final SpaceDetailsModel space;
|
final SpaceDetailsModel space;
|
||||||
final void Function(SpaceDetailsModel space) onSave;
|
final void Function(SpaceDetailsModel space) onSave;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceDetailsForm> createState() => _SpaceDetailsFormState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceDetailsFormState extends State<SpaceDetailsForm> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
bool _isFormValid = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
final initialName = widget.space.spaceName;
|
||||||
|
_isFormValid = initialName.isNotEmpty &&
|
||||||
|
initialName.length <= 50 &&
|
||||||
|
!widget.space.subspaces.any((subspace) => subspace.name == initialName);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => SpaceDetailsModelBloc(initialState: space),
|
create: (context) => SpaceDetailsModelBloc(initialState: widget.space),
|
||||||
child: BlocBuilder<SpaceDetailsModelBloc, SpaceDetailsModel>(
|
child: BlocBuilder<SpaceDetailsModelBloc, SpaceDetailsModel>(
|
||||||
buildWhen: (previous, current) => previous != current,
|
buildWhen: (previous, current) => previous != current,
|
||||||
builder: (context, space) {
|
builder: (context, space) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: DefaultTextStyle(
|
title: DefaultTextStyle(
|
||||||
style: context.textTheme.titleLarge!.copyWith(
|
style: context.textTheme.titleLarge!.copyWith(
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
|
||||||
child: title,
|
|
||||||
),
|
),
|
||||||
backgroundColor: ColorsManager.white,
|
child: widget.title,
|
||||||
content: SizedBox(
|
),
|
||||||
|
backgroundColor: ColorsManager.white,
|
||||||
|
content: Form(
|
||||||
|
key: _formKey,
|
||||||
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||||
|
onChanged: () {
|
||||||
|
final isValid = _formKey.currentState?.validate() ?? false;
|
||||||
|
if (_isFormValid != isValid) {
|
||||||
|
setState(() {
|
||||||
|
_isFormValid = isValid;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
height: context.screenHeight * 0.3,
|
height: context.screenHeight * 0.3,
|
||||||
width: context.screenWidth * 0.4,
|
width: context.screenWidth * 0.4,
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -70,14 +98,16 @@ class SpaceDetailsForm extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
),
|
||||||
SpaceDetailsActionButtons(
|
actions: [
|
||||||
onSave: () => onSave(space),
|
SpaceDetailsActionButtons(
|
||||||
onCancel: Navigator.of(context).pop,
|
onSave: _isFormValid ? () => widget.onSave(space) : null,
|
||||||
),
|
onCancel: Navigator.of(context).pop,
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
}),
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,13 @@ class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
|
||||||
|
|
||||||
String? _validateName(String? value) {
|
String? _validateName(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return '*Space name should not be empty.';
|
return '*Space name should not be empty.';
|
||||||
}
|
}
|
||||||
|
if (value.length > 50) {
|
||||||
|
return '*Space name cannot be longer than 50 characters.';
|
||||||
|
}
|
||||||
if (widget.isNameFieldExist(value)) {
|
if (widget.isNameFieldExist(value)) {
|
||||||
return '*Name already exists';
|
return '*Name already exists';
|
||||||
}
|
}
|
||||||
@ -47,30 +48,26 @@ class _SpaceNameTextFieldState extends State<SpaceNameTextField> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Form(
|
return TextFormField(
|
||||||
key: _formKey,
|
controller: _controller,
|
||||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
|
||||||
child: TextFormField(
|
UpdateSpaceDetailsName(value),
|
||||||
controller: _controller,
|
|
||||||
onChanged: (value) => context.read<SpaceDetailsModelBloc>().add(
|
|
||||||
UpdateSpaceDetailsName(value),
|
|
||||||
),
|
|
||||||
validator: _validateName,
|
|
||||||
style: context.textTheme.bodyMedium,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: 'Please enter the name',
|
|
||||||
hintStyle: context.textTheme.bodyMedium!.copyWith(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
),
|
|
||||||
filled: true,
|
|
||||||
fillColor: ColorsManager.boxColor,
|
|
||||||
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
|
|
||||||
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
|
|
||||||
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
|
||||||
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
|
||||||
errorStyle: context.textTheme.bodySmall?.copyWith(
|
|
||||||
color: context.theme.colorScheme.error,
|
|
||||||
),
|
),
|
||||||
|
validator: _validateName,
|
||||||
|
style: context.textTheme.bodyMedium,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Please enter the name',
|
||||||
|
hintStyle: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
),
|
||||||
|
filled: true,
|
||||||
|
fillColor: ColorsManager.boxColor,
|
||||||
|
enabledBorder: _buildBorder(context, ColorsManager.vividBlue),
|
||||||
|
focusedBorder: _buildBorder(context, ColorsManager.primaryColor),
|
||||||
|
errorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||||
|
focusedErrorBorder: _buildBorder(context, context.theme.colorScheme.error),
|
||||||
|
errorStyle: context.textTheme.bodySmall?.copyWith(
|
||||||
|
color: context.theme.colorScheme.error,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -89,28 +89,35 @@ class _SpaceSubSpacesDialogState extends State<SpaceSubSpacesDialog> {
|
|||||||
),
|
),
|
||||||
child: const SelectableText('Create Sub-Space'),
|
child: const SelectableText('Create Sub-Space'),
|
||||||
),
|
),
|
||||||
content: Column(
|
content: ConstrainedBox(
|
||||||
spacing: 12,
|
constraints: BoxConstraints(
|
||||||
mainAxisSize: MainAxisSize.min,
|
maxHeight: context.screenHeight * 0.4,
|
||||||
children: [
|
),
|
||||||
SubSpacesInput(
|
child: SingleChildScrollView(
|
||||||
subSpaces: _subspaces,
|
child: Column(
|
||||||
onSubspaceAdded: _handleSubspaceAdded,
|
spacing: 12,
|
||||||
onSubspaceDeleted: _handleSubspaceDeleted,
|
mainAxisSize: MainAxisSize.min,
|
||||||
controller: _subspaceNameController,
|
children: [
|
||||||
),
|
SubSpacesInput(
|
||||||
AnimatedSwitcher(
|
subSpaces: _subspaces,
|
||||||
duration: const Duration(milliseconds: 100),
|
onSubspaceAdded: _handleSubspaceAdded,
|
||||||
child: Visibility(
|
onSubspaceDeleted: _handleSubspaceDeleted,
|
||||||
key: ValueKey(_hasDuplicateNames),
|
controller: _subspaceNameController,
|
||||||
visible: _hasDuplicateNames,
|
|
||||||
child: const SelectableText(
|
|
||||||
'Error: Duplicate subspace names are not allowed.',
|
|
||||||
style: TextStyle(color: Colors.red),
|
|
||||||
),
|
),
|
||||||
),
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
child: Visibility(
|
||||||
|
key: ValueKey(_hasDuplicateNames),
|
||||||
|
visible: _hasDuplicateNames,
|
||||||
|
child: const SelectableText(
|
||||||
|
'Error: Duplicate subspace names are not allowed.',
|
||||||
|
style: TextStyle(color: Colors.red),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
SpaceDetailsActionButtons(
|
SpaceDetailsActionButtons(
|
||||||
|
@ -182,15 +182,20 @@ class _AssignTagsDialogState extends State<AssignTagsDialog> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
ConstrainedBox(
|
||||||
width: double.infinity,
|
constraints: BoxConstraints(
|
||||||
child: AssignTagsTable(
|
minWidth: double.infinity,
|
||||||
productAllocations: allProductAllocations,
|
maxHeight: context.screenHeight * 0.6,
|
||||||
subspaces: _space.subspaces,
|
),
|
||||||
productLocations: productLocations,
|
child: SingleChildScrollView(
|
||||||
onTagSelected: _handleTagChange,
|
child: AssignTagsTable(
|
||||||
onLocationSelected: _handleLocationChange,
|
productAllocations: allProductAllocations,
|
||||||
onProductDeleted: _handleProductDelete,
|
subspaces: _space.subspaces,
|
||||||
|
productLocations: productLocations,
|
||||||
|
onTagSelected: _handleTagChange,
|
||||||
|
onLocationSelected: _handleLocationChange,
|
||||||
|
onProductDeleted: _handleProductDelete,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasErrors)
|
if (hasErrors)
|
||||||
|
Reference in New Issue
Block a user