mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-14 17:25:50 +00:00
Compare commits
3 Commits
SP-1474-FE
...
SP-1387-fe
Author | SHA1 | Date | |
---|---|---|---|
388391eec4 | |||
ad00cf35ba | |||
1200a809c2 |
@ -62,7 +62,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
|
|
||||||
final buttonLabel =
|
final buttonLabel =
|
||||||
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||||
|
final isAnyDeviceOffline =
|
||||||
|
selectedDevices.any((element) => !(element.online ?? false));
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: SpaceTreeView(
|
Expanded(child: SpaceTreeView(
|
||||||
@ -103,8 +104,28 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
decoration: containerDecoration,
|
decoration: containerDecoration,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
|
backgroundColor: isAnyDeviceOffline
|
||||||
|
? ColorsManager.primaryColor
|
||||||
|
.withValues(alpha: 0.1)
|
||||||
|
: null,
|
||||||
onPressed: isControlButtonEnabled
|
onPressed: isControlButtonEnabled
|
||||||
? () {
|
? () {
|
||||||
|
if (isAnyDeviceOffline) {
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.clearSnackBars();
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
.showSnackBar(
|
||||||
|
const SnackBar(
|
||||||
|
content: Text(
|
||||||
|
'This Device is Offline',
|
||||||
|
),
|
||||||
|
duration:
|
||||||
|
Duration(seconds: 2),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedDevices.length == 1) {
|
if (selectedDevices.length == 1) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
class SpaceConnectionModel {
|
|
||||||
final String from;
|
|
||||||
final String to;
|
|
||||||
|
|
||||||
const SpaceConnectionModel({required this.from, required this.to});
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpacesConnectionsArrowPainter extends CustomPainter {
|
|
||||||
final List<SpaceConnectionModel> connections;
|
|
||||||
final Map<String, Offset> positions;
|
|
||||||
final double cardWidth = 150.0;
|
|
||||||
final double cardHeight = 90.0;
|
|
||||||
final String? selectedSpaceUuid;
|
|
||||||
|
|
||||||
SpacesConnectionsArrowPainter({
|
|
||||||
required this.connections,
|
|
||||||
required this.positions,
|
|
||||||
this.selectedSpaceUuid,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
void paint(Canvas canvas, Size size) {
|
|
||||||
for (final connection in connections) {
|
|
||||||
final isSelected = connection.to == selectedSpaceUuid;
|
|
||||||
final paint = Paint()
|
|
||||||
..color = isSelected
|
|
||||||
? ColorsManager.primaryColor
|
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
|
||||||
..strokeWidth = 2.0
|
|
||||||
..style = PaintingStyle.stroke;
|
|
||||||
|
|
||||||
final from = positions[connection.from];
|
|
||||||
final to = positions[connection.to];
|
|
||||||
|
|
||||||
if (from != null && to != null) {
|
|
||||||
final startPoint =
|
|
||||||
Offset(from.dx + cardWidth / 2, from.dy + cardHeight - 10);
|
|
||||||
final endPoint = Offset(to.dx + cardWidth / 2, to.dy);
|
|
||||||
|
|
||||||
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
|
||||||
|
|
||||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 60);
|
|
||||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60);
|
|
||||||
|
|
||||||
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
|
||||||
controlPoint2.dy, endPoint.dx, endPoint.dy);
|
|
||||||
|
|
||||||
canvas.drawPath(path, paint);
|
|
||||||
|
|
||||||
final circlePaint = Paint()
|
|
||||||
..color = isSelected
|
|
||||||
? ColorsManager.primaryColor
|
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
|
||||||
..style = PaintingStyle.fill
|
|
||||||
..blendMode = BlendMode.srcIn;
|
|
||||||
canvas.drawCircle(endPoint, 4, circlePaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart';
|
|
||||||
|
|
||||||
abstract final class SpaceManagementCommunityDialogHelper {
|
|
||||||
static void showCreateDialog(BuildContext context) {
|
|
||||||
showDialog<void>(
|
|
||||||
context: context,
|
|
||||||
builder: (_) => CreateCommunityDialog(
|
|
||||||
title: const SelectableText('Community Name'),
|
|
||||||
onCreateCommunity: (community) {
|
|
||||||
context.read<CommunitiesBloc>().add(
|
|
||||||
InsertCommunity(community),
|
|
||||||
);
|
|
||||||
context.read<CommunitiesTreeSelectionBloc>().add(
|
|
||||||
SelectCommunityEvent(community: community),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,236 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/models/space_connection_model.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/painters/spaces_connections_arrow_painter.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_card_widget.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_cell.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';
|
|
||||||
|
|
||||||
class CommunityStructureCanvas extends StatefulWidget {
|
|
||||||
const CommunityStructureCanvas({
|
|
||||||
required this.community,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
final CommunityModel community;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CommunityStructureCanvas> createState() =>_CommunityStructureCanvasState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
final Map<String, Offset> _positions = {};
|
|
||||||
final double _cardWidth = 150.0;
|
|
||||||
final double _cardHeight = 90.0;
|
|
||||||
final double _horizontalSpacing = 150.0;
|
|
||||||
final double _verticalSpacing = 120.0;
|
|
||||||
String? _selectedSpaceUuid;
|
|
||||||
|
|
||||||
late TransformationController _transformationController;
|
|
||||||
late AnimationController _animationController;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_transformationController = TransformationController();
|
|
||||||
_animationController = AnimationController(
|
|
||||||
vsync: this,
|
|
||||||
duration: const Duration(milliseconds: 100),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_transformationController.dispose();
|
|
||||||
_animationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _runAnimation(Matrix4 target) {
|
|
||||||
final animation = Matrix4Tween(
|
|
||||||
begin: _transformationController.value,
|
|
||||||
end: target,
|
|
||||||
).animate(_animationController);
|
|
||||||
|
|
||||||
void listener() {
|
|
||||||
_transformationController.value = animation.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
animation.addListener(listener);
|
|
||||||
_animationController.forward(from: 0).whenCompleteOrCancel(() {
|
|
||||||
animation.removeListener(listener);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSpaceTapped(String spaceUuid) {
|
|
||||||
setState(() {
|
|
||||||
_selectedSpaceUuid = spaceUuid;
|
|
||||||
});
|
|
||||||
|
|
||||||
final position = _positions[spaceUuid];
|
|
||||||
if (position == null) return;
|
|
||||||
|
|
||||||
const scale = 2.0;
|
|
||||||
final viewSize = context.size;
|
|
||||||
if (viewSize == null) return;
|
|
||||||
|
|
||||||
final x = -position.dx * scale + (viewSize.width / 2) - (_cardWidth * scale / 2);
|
|
||||||
final y =
|
|
||||||
-position.dy * scale + (viewSize.height / 2) - (_cardHeight * scale / 2);
|
|
||||||
|
|
||||||
final matrix = Matrix4.identity()
|
|
||||||
..translate(x, y)
|
|
||||||
..scale(scale);
|
|
||||||
|
|
||||||
_runAnimation(matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _resetSelectionAndZoom() {
|
|
||||||
setState(() {
|
|
||||||
_selectedSpaceUuid = null;
|
|
||||||
});
|
|
||||||
_runAnimation(Matrix4.identity());
|
|
||||||
}
|
|
||||||
|
|
||||||
void _calculateLayout(
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
int depth,
|
|
||||||
Map<int, double> levelXOffset,
|
|
||||||
) {
|
|
||||||
for (final space in spaces) {
|
|
||||||
double childSubtreeWidth = 0;
|
|
||||||
if (space.children.isNotEmpty) {
|
|
||||||
_calculateLayout(space.children, depth + 1, levelXOffset);
|
|
||||||
final firstChildPos = _positions[space.children.first.uuid];
|
|
||||||
final lastChildPos = _positions[space.children.last.uuid];
|
|
||||||
if (firstChildPos != null && lastChildPos != null) {
|
|
||||||
childSubtreeWidth = (lastChildPos.dx + _cardWidth) - firstChildPos.dx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentX = levelXOffset.putIfAbsent(depth, () => 0.0);
|
|
||||||
double? x;
|
|
||||||
|
|
||||||
if (space.children.isNotEmpty) {
|
|
||||||
final firstChildPos = _positions[space.children.first.uuid]!;
|
|
||||||
x = firstChildPos.dx + (childSubtreeWidth - _cardWidth) / 2;
|
|
||||||
} else {
|
|
||||||
x = currentX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x < currentX) {
|
|
||||||
final shiftX = currentX - x;
|
|
||||||
_shiftSubtree(space, shiftX);
|
|
||||||
final keysToShift = levelXOffset.keys.where((d) => d > depth).toList();
|
|
||||||
for (final key in keysToShift) {
|
|
||||||
levelXOffset[key] = levelXOffset[key]! + shiftX;
|
|
||||||
}
|
|
||||||
x += shiftX;
|
|
||||||
}
|
|
||||||
|
|
||||||
final y = depth * (_verticalSpacing + _cardHeight);
|
|
||||||
_positions[space.uuid] = Offset(x, y);
|
|
||||||
levelXOffset[depth] = x + _cardWidth + _horizontalSpacing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _shiftSubtree(SpaceModel space, double shiftX) {
|
|
||||||
if (_positions.containsKey(space.uuid)) {
|
|
||||||
_positions[space.uuid] = _positions[space.uuid]!.translate(shiftX, 0);
|
|
||||||
}
|
|
||||||
for (final child in space.children) {
|
|
||||||
_shiftSubtree(child, shiftX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildTreeWidgets() {
|
|
||||||
_positions.clear();
|
|
||||||
final community = widget.community;
|
|
||||||
|
|
||||||
_calculateLayout(community.spaces, 0, {});
|
|
||||||
|
|
||||||
final widgets = <Widget>[];
|
|
||||||
final connections = <SpaceConnectionModel>[];
|
|
||||||
_generateWidgets(community.spaces, widgets, connections);
|
|
||||||
|
|
||||||
return [
|
|
||||||
CustomPaint(
|
|
||||||
painter: SpacesConnectionsArrowPainter(
|
|
||||||
connections: connections,
|
|
||||||
positions: _positions,
|
|
||||||
selectedSpaceUuid: _selectedSpaceUuid,
|
|
||||||
),
|
|
||||||
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
void _generateWidgets(
|
|
||||||
List<SpaceModel> spaces,
|
|
||||||
List<Widget> widgets,
|
|
||||||
List<SpaceConnectionModel> connections,
|
|
||||||
) {
|
|
||||||
for (final space in spaces) {
|
|
||||||
final position = _positions[space.uuid];
|
|
||||||
if (position == null) continue;
|
|
||||||
|
|
||||||
widgets.add(
|
|
||||||
Positioned(
|
|
||||||
left: position.dx,
|
|
||||||
top: position.dy,
|
|
||||||
width: _cardWidth,
|
|
||||||
height: _cardHeight,
|
|
||||||
child: SpaceCardWidget(
|
|
||||||
index: spaces.indexOf(space),
|
|
||||||
onPositionChanged: (newPosition) {},
|
|
||||||
buildSpaceContainer: (index) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: 1.0,
|
|
||||||
child: SpaceCell(
|
|
||||||
index: index,
|
|
||||||
onTap: () => _onSpaceTapped(space.uuid),
|
|
||||||
icon: space.icon,
|
|
||||||
name: space.spaceName,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
screenSize: MediaQuery.sizeOf(context),
|
|
||||||
position: position,
|
|
||||||
isHovered: false,
|
|
||||||
onHoverChanged: (int index, bool isHovered) {},
|
|
||||||
onButtonTap: (int index, Offset newPosition) {},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
for (final child in space.children) {
|
|
||||||
connections.add(SpaceConnectionModel(from: space.uuid, to: child.uuid));
|
|
||||||
}
|
|
||||||
_generateWidgets(space.children, widgets, connections);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final treeWidgets = _buildTreeWidgets();
|
|
||||||
return InteractiveViewer(
|
|
||||||
transformationController: _transformationController,
|
|
||||||
boundaryMargin: EdgeInsets.symmetric(
|
|
||||||
horizontal: MediaQuery.sizeOf(context).width * 0.3,
|
|
||||||
vertical: MediaQuery.sizeOf(context).height * 0.2,
|
|
||||||
),
|
|
||||||
minScale: 0.5,
|
|
||||||
maxScale: 3.0,
|
|
||||||
constrained: false,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: _resetSelectionAndZoom,
|
|
||||||
child: SizedBox(
|
|
||||||
width: MediaQuery.sizeOf(context).width * 2,
|
|
||||||
height: MediaQuery.sizeOf(context).height * 2,
|
|
||||||
child: Stack(children: treeWidgets),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class CommunityTemplateCell extends StatelessWidget {
|
|
||||||
const CommunityTemplateCell({
|
|
||||||
super.key,
|
|
||||||
required this.onTap,
|
|
||||||
required this.title,
|
|
||||||
});
|
|
||||||
|
|
||||||
final void Function() onTap;
|
|
||||||
final Widget title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Column(
|
|
||||||
spacing: 8,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 2.0,
|
|
||||||
child: Container(
|
|
||||||
decoration: ShapeDecoration(
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
side: const BorderSide(
|
|
||||||
width: 4,
|
|
||||||
strokeAlign: BorderSide.strokeAlignOutside,
|
|
||||||
color: ColorsManager.borderColor,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
DefaultTextStyle(
|
|
||||||
style: context.textTheme.bodyLarge!.copyWith(
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
|
||||||
child: title,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class CreateSpaceButton extends StatelessWidget {
|
|
||||||
const CreateSpaceButton({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {},
|
|
||||||
child: Container(
|
|
||||||
height: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.grey.withValues(alpha: 0.5),
|
|
||||||
spreadRadius: 5,
|
|
||||||
blurRadius: 7,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Container(
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.add,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class PlusButtonWidget extends StatelessWidget {
|
|
||||||
final int index;
|
|
||||||
final String direction;
|
|
||||||
final Offset offset;
|
|
||||||
final void Function(int index, Offset newPosition) onButtonTap;
|
|
||||||
|
|
||||||
const PlusButtonWidget({
|
|
||||||
super.key,
|
|
||||||
required this.index,
|
|
||||||
required this.direction,
|
|
||||||
required this.offset,
|
|
||||||
required this.onButtonTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (direction == 'down') {
|
|
||||||
onButtonTap(index, const Offset(0, 150));
|
|
||||||
} else {
|
|
||||||
onButtonTap(index, const Offset(150, 0));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(
|
|
||||||
Icons.add,
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/plus_button_widget.dart';
|
|
||||||
|
|
||||||
class SpaceCardWidget extends StatelessWidget {
|
|
||||||
final int index;
|
|
||||||
final Size screenSize;
|
|
||||||
final Offset position;
|
|
||||||
final bool isHovered;
|
|
||||||
final void Function(int index, bool isHovered) onHoverChanged;
|
|
||||||
final void Function(int index, Offset newPosition) onButtonTap;
|
|
||||||
final Widget Function(int index) buildSpaceContainer;
|
|
||||||
final ValueChanged<Offset> onPositionChanged;
|
|
||||||
|
|
||||||
const SpaceCardWidget({
|
|
||||||
super.key,
|
|
||||||
required this.index,
|
|
||||||
required this.onPositionChanged,
|
|
||||||
required this.screenSize,
|
|
||||||
required this.position,
|
|
||||||
required this.isHovered,
|
|
||||||
required this.onHoverChanged,
|
|
||||||
required this.onButtonTap,
|
|
||||||
required this.buildSpaceContainer,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MouseRegion(
|
|
||||||
onEnter: (_) => onHoverChanged(index, true),
|
|
||||||
onExit: (_) => onHoverChanged(index, false),
|
|
||||||
child: SizedBox(
|
|
||||||
width: 150,
|
|
||||||
height: 90,
|
|
||||||
child: Stack(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
alignment: Alignment.center,
|
|
||||||
children: [
|
|
||||||
buildSpaceContainer(index),
|
|
||||||
|
|
||||||
if (isHovered)
|
|
||||||
Positioned(
|
|
||||||
bottom: 0,
|
|
||||||
child: PlusButtonWidget(
|
|
||||||
index: index,
|
|
||||||
direction: 'down',
|
|
||||||
offset: Offset.zero,
|
|
||||||
onButtonTap: onButtonTap,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isHovered)
|
|
||||||
Positioned(
|
|
||||||
right: -15,
|
|
||||||
child: PlusButtonWidget(
|
|
||||||
index: index,
|
|
||||||
direction: 'right',
|
|
||||||
offset: Offset.zero,
|
|
||||||
onButtonTap: onButtonTap,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_svg/svg.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpaceCell extends StatelessWidget {
|
|
||||||
final int index;
|
|
||||||
final String icon;
|
|
||||||
final String name;
|
|
||||||
final VoidCallback? onDoubleTap;
|
|
||||||
final VoidCallback? onTap;
|
|
||||||
|
|
||||||
const SpaceCell({
|
|
||||||
super.key,
|
|
||||||
required this.index,
|
|
||||||
required this.icon,
|
|
||||||
required this.name,
|
|
||||||
this.onTap,
|
|
||||||
this.onDoubleTap,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
return GestureDetector(
|
|
||||||
onDoubleTap: onDoubleTap,
|
|
||||||
onTap: onTap,
|
|
||||||
child: Container(
|
|
||||||
width: 150,
|
|
||||||
height: 70,
|
|
||||||
decoration: _containerDecoration(),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
_buildIconContainer(),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
name,
|
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildIconContainer() {
|
|
||||||
return Container(
|
|
||||||
width: 40,
|
|
||||||
height: double.infinity,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(15),
|
|
||||||
bottomLeft: Radius.circular(15),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
icon,
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
BoxDecoration _containerDecoration() {
|
|
||||||
return BoxDecoration(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: ColorsManager.lightGrayColor.withValues(alpha: 0.5),
|
|
||||||
spreadRadius: 2,
|
|
||||||
blurRadius: 5,
|
|
||||||
offset: const Offset(0, 3),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_management_community_structure.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/space_management_templates_view.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/widgets/space_management_communities_tree.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/widgets/space_management_communities_tree.dart';
|
||||||
|
|
||||||
class SpaceManagementBody extends StatelessWidget {
|
class SpaceManagementBody extends StatelessWidget {
|
||||||
@ -10,21 +6,9 @@ class SpaceManagementBody extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return const Row(
|
||||||
children: [
|
children: [
|
||||||
const SpaceManagementCommunitiesTree(),
|
SpaceManagementCommunitiesTree(),
|
||||||
Expanded(
|
|
||||||
child: BlocBuilder<CommunitiesTreeSelectionBloc,
|
|
||||||
CommunitiesTreeSelectionState>(
|
|
||||||
buildWhen: (previous, current) =>
|
|
||||||
previous.selectedCommunity != current.selectedCommunity,
|
|
||||||
builder: (context, state) => Visibility(
|
|
||||||
visible: state.selectedCommunity == null,
|
|
||||||
replacement: const SpaceManagementCommunityStructure(),
|
|
||||||
child: const SpaceManagementTemplatesView(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_canvas.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/create_space_button.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
|
||||||
|
|
||||||
class SpaceManagementCommunityStructure extends StatelessWidget {
|
|
||||||
const SpaceManagementCommunityStructure({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final selectedCommunity =
|
|
||||||
context.watch<CommunitiesTreeSelectionBloc>().state.selectedCommunity!;
|
|
||||||
const spacer = Spacer(flex: 10);
|
|
||||||
return Visibility(
|
|
||||||
visible: selectedCommunity.spaces.isNotEmpty,
|
|
||||||
replacement: const Row(
|
|
||||||
children: [spacer, Expanded(child: CreateSpaceButton()), spacer]),
|
|
||||||
child: CommunityStructureCanvas(community: selectedCommunity),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_template_cell.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
class SpaceManagementTemplatesView extends StatelessWidget {
|
|
||||||
const SpaceManagementTemplatesView({super.key});
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Expanded(
|
|
||||||
child: ColoredBox(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
child: GridView.builder(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
|
||||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
|
||||||
maxCrossAxisExtent: 400,
|
|
||||||
mainAxisSpacing: 10,
|
|
||||||
crossAxisSpacing: 10,
|
|
||||||
childAspectRatio: 2.0,
|
|
||||||
),
|
|
||||||
itemCount: _gridItems(context).length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final model = _gridItems(context)[index];
|
|
||||||
return CommunityTemplateCell(
|
|
||||||
onTap: model.onTap,
|
|
||||||
title: model.title,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<_CommunityTemplateModel> _gridItems(BuildContext context) {
|
|
||||||
return [
|
|
||||||
_CommunityTemplateModel(
|
|
||||||
title: const Text('Blank'),
|
|
||||||
onTap: () => SpaceManagementCommunityDialogHelper.showCreateDialog(context),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CommunityTemplateModel {
|
|
||||||
final Widget title;
|
|
||||||
final void Function() onTap;
|
|
||||||
|
|
||||||
_CommunityTemplateModel({
|
|
||||||
required this.title,
|
|
||||||
required this.onTap,
|
|
||||||
});
|
|
||||||
}
|
|
@ -32,7 +32,7 @@ class CommunitiesTreeSelectionBloc
|
|||||||
) {
|
) {
|
||||||
emit(
|
emit(
|
||||||
CommunitiesTreeSelectionState(
|
CommunitiesTreeSelectionState(
|
||||||
selectedCommunity: event.community,
|
selectedCommunity: null,
|
||||||
selectedSpace: event.space,
|
selectedSpace: event.space,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,7 +8,7 @@ sealed class CommunitiesTreeSelectionEvent extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class SelectCommunityEvent extends CommunitiesTreeSelectionEvent {
|
final class SelectCommunityEvent extends CommunitiesTreeSelectionEvent {
|
||||||
final CommunityModel community;
|
final CommunityModel? community;
|
||||||
|
|
||||||
const SelectCommunityEvent({required this.community});
|
const SelectCommunityEvent({required this.community});
|
||||||
@override
|
@override
|
||||||
@ -16,10 +16,9 @@ final class SelectCommunityEvent extends CommunitiesTreeSelectionEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
||||||
final SpaceModel space;
|
final SpaceModel? space;
|
||||||
final CommunityModel community;
|
|
||||||
|
|
||||||
const SelectSpaceEvent({required this.space, required this.community});
|
const SelectSpaceEvent({required this.space});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [space];
|
List<Object?> get props => [space];
|
||||||
|
@ -30,7 +30,7 @@ class SpaceManagementCommunitiesTreeSpaceTile extends StatelessWidget {
|
|||||||
initiallyExpanded: spaceIsExpanded,
|
initiallyExpanded: spaceIsExpanded,
|
||||||
onExpansionChanged: (expanded) {},
|
onExpansionChanged: (expanded) {},
|
||||||
onItemSelected: () => context.read<CommunitiesTreeSelectionBloc>().add(
|
onItemSelected: () => context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
SelectSpaceEvent(community: community, space: space),
|
SelectSpaceEvent(space: space),
|
||||||
),
|
),
|
||||||
children: space.children
|
children: space.children
|
||||||
.map(
|
.map(
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
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/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/widgets/space_management_sidebar_add_community_button.dart';
|
import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/widgets/space_management_sidebar_add_community_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.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 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
@ -40,7 +41,7 @@ class SpaceManagementSidebarHeader extends StatelessWidget {
|
|||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
_clearSelection(context);
|
_clearSelection(context);
|
||||||
} else {
|
} else {
|
||||||
SpaceManagementCommunityDialogHelper.showCreateDialog(context);
|
_showCreateCommunityDialog(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,4 +50,19 @@ class SpaceManagementSidebarHeader extends StatelessWidget {
|
|||||||
const ClearCommunitiesTreeSelectionEvent(),
|
const ClearCommunitiesTreeSelectionEvent(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showCreateCommunityDialog(BuildContext context) => showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => CreateCommunityDialog(
|
||||||
|
title: const Text('Community Name'),
|
||||||
|
onCreateCommunity: (community) {
|
||||||
|
context.read<CommunitiesBloc>().add(
|
||||||
|
InsertCommunity(community),
|
||||||
|
);
|
||||||
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectCommunityEvent(community: community),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class RemoteCreateCommunityService implements CreateCommunityService {
|
|||||||
return _defaultErrorMessage;
|
return _defaultErrorMessage;
|
||||||
}
|
}
|
||||||
final error = body['error'] as Map<String, dynamic>?;
|
final error = body['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['message'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +41,11 @@ class CreateCommunityDialog extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
onCreateCommunity.call(community);
|
onCreateCommunity.call(community);
|
||||||
break;
|
break;
|
||||||
case CreateCommunityFailure():
|
case CreateCommunityFailure(:final message):
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text(message)),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
|
||||||
|
|
||||||
class AccessTypeRadioGroup extends StatelessWidget {
|
|
||||||
final String? selectedType;
|
|
||||||
final String? accessTypeSelected;
|
|
||||||
final Function(String) onTypeSelected;
|
|
||||||
final VisitorPasswordBloc visitorBloc;
|
|
||||||
|
|
||||||
const AccessTypeRadioGroup({
|
|
||||||
super.key,
|
|
||||||
required this.selectedType,
|
|
||||||
required this.accessTypeSelected,
|
|
||||||
required this.onTypeSelected,
|
|
||||||
required this.visitorBloc,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Size size = MediaQuery.of(context).size;
|
|
||||||
final text = Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: Colors.black, fontSize: 13);
|
|
||||||
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'* ',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: Colors.red),
|
|
||||||
),
|
|
||||||
Text('Access Type', style: text),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
if (size.width < 800)
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Online Password',
|
|
||||||
selectedType ?? accessTypeSelected,
|
|
||||||
onTypeSelected,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Offline Password',
|
|
||||||
selectedType ?? accessTypeSelected,
|
|
||||||
onTypeSelected,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Online Password',
|
|
||||||
selectedType ?? accessTypeSelected,
|
|
||||||
onTypeSelected,
|
|
||||||
width: size.width * 0.12,
|
|
||||||
),
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Offline Password',
|
|
||||||
selectedType ?? accessTypeSelected,
|
|
||||||
onTypeSelected,
|
|
||||||
width: size.width * 0.12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(flex: 2),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRadioTile(
|
|
||||||
BuildContext context,
|
|
||||||
String value,
|
|
||||||
String? groupValue,
|
|
||||||
Function(String) onChanged, {
|
|
||||||
double? width,
|
|
||||||
}) {
|
|
||||||
return SizedBox(
|
|
||||||
width: width,
|
|
||||||
child: RadioListTile<String>(
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
title: Text(value,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: 13,
|
|
||||||
)),
|
|
||||||
value: value,
|
|
||||||
groupValue: groupValue,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != null) {
|
|
||||||
onChanged(value);
|
|
||||||
if (value == 'Dynamic Password') {
|
|
||||||
visitorBloc.usageFrequencySelected = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart';
|
|
||||||
|
|
||||||
class NameAndEmailFields extends StatelessWidget {
|
|
||||||
final TextEditingController nameController;
|
|
||||||
final TextEditingController emailController;
|
|
||||||
final String? Function(String?)? nameValidator;
|
|
||||||
final String? Function(String?)? emailValidator;
|
|
||||||
|
|
||||||
const NameAndEmailFields({
|
|
||||||
super.key,
|
|
||||||
required this.nameController,
|
|
||||||
required this.emailController,
|
|
||||||
required this.nameValidator,
|
|
||||||
required this.emailValidator,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Size size = MediaQuery.of(context).size;
|
|
||||||
return Container(
|
|
||||||
width: size.width,
|
|
||||||
child: size.width < 800
|
|
||||||
? Column(
|
|
||||||
children: [
|
|
||||||
CustomWebTextField(
|
|
||||||
validator: nameValidator,
|
|
||||||
controller: nameController,
|
|
||||||
isRequired: true,
|
|
||||||
textFieldName: 'Name',
|
|
||||||
description: '',
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
CustomWebTextField(
|
|
||||||
validator: emailValidator,
|
|
||||||
controller: emailController,
|
|
||||||
isRequired: true,
|
|
||||||
textFieldName: 'Email Address',
|
|
||||||
description:
|
|
||||||
'The password will be sent to the visitor’s email address.',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: CustomWebTextField(
|
|
||||||
validator: nameValidator,
|
|
||||||
controller: nameController,
|
|
||||||
isRequired: true,
|
|
||||||
textFieldName: 'Name',
|
|
||||||
description: '',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: CustomWebTextField(
|
|
||||||
validator: emailValidator,
|
|
||||||
controller: emailController,
|
|
||||||
isRequired: true,
|
|
||||||
textFieldName: 'Email Address',
|
|
||||||
description:
|
|
||||||
'The password will be sent to the visitor’s email address.',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class UsageFrequencyRadioGroup extends StatelessWidget {
|
|
||||||
final String? selectedFrequency;
|
|
||||||
final String? usageFrequencySelected;
|
|
||||||
final Function(String) onFrequencySelected;
|
|
||||||
|
|
||||||
const UsageFrequencyRadioGroup({
|
|
||||||
super.key,
|
|
||||||
required this.selectedFrequency,
|
|
||||||
required this.usageFrequencySelected,
|
|
||||||
required this.onFrequencySelected,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Size size = MediaQuery.of(context).size;
|
|
||||||
final text = Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: Colors.black, fontSize: 13);
|
|
||||||
|
|
||||||
return size.width < 600
|
|
||||||
? Column(
|
|
||||||
children: [
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'One-Time',
|
|
||||||
selectedFrequency ?? usageFrequencySelected,
|
|
||||||
onFrequencySelected,
|
|
||||||
text: text,
|
|
||||||
fullWidth: true,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Periodic',
|
|
||||||
selectedFrequency ?? usageFrequencySelected,
|
|
||||||
onFrequencySelected,
|
|
||||||
text: text,
|
|
||||||
fullWidth: true,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: Row(
|
|
||||||
children: [
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'One-Time',
|
|
||||||
selectedFrequency ?? usageFrequencySelected,
|
|
||||||
onFrequencySelected,
|
|
||||||
width: size.width * 0.12,
|
|
||||||
text: text,
|
|
||||||
),
|
|
||||||
_buildRadioTile(
|
|
||||||
context,
|
|
||||||
'Periodic',
|
|
||||||
selectedFrequency ?? usageFrequencySelected,
|
|
||||||
onFrequencySelected,
|
|
||||||
width: size.width * 0.12,
|
|
||||||
text: text,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRadioTile(
|
|
||||||
BuildContext context,
|
|
||||||
String value,
|
|
||||||
String? groupValue,
|
|
||||||
Function(String) onChanged, {
|
|
||||||
double? width,
|
|
||||||
required TextStyle text,
|
|
||||||
bool fullWidth = false,
|
|
||||||
}) {
|
|
||||||
return SizedBox(
|
|
||||||
width: fullWidth ? double.infinity : width,
|
|
||||||
child: RadioListTile<String>(
|
|
||||||
contentPadding: EdgeInsets.zero,
|
|
||||||
title: Text(value, style: text),
|
|
||||||
value: value,
|
|
||||||
groupValue: groupValue,
|
|
||||||
onChanged: (String? value) {
|
|
||||||
if (value != null) {
|
|
||||||
onChanged(value);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,11 +9,8 @@ import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart';
|
|||||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
|
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
|
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/access_type_radio_group.dart';
|
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart';
|
import 'package:syncrow_web/pages/visitor_password/view/add_device_dialog.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/repeat_widget.dart';
|
import 'package:syncrow_web/pages/visitor_password/view/repeat_widget.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/responsive_fields_row.dart';
|
|
||||||
import 'package:syncrow_web/pages/visitor_password/view/usage_frequency_radio_group.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/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
@ -24,10 +21,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Size size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
var text = Theme.of(context)
|
var text = Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.black, fontSize: 13);
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: Colors.black, fontSize: 13);
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
child: BlocListener<VisitorPasswordBloc, VisitorPasswordState>(
|
child: BlocListener<VisitorPasswordBloc, VisitorPasswordState>(
|
||||||
@ -41,8 +35,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
title: 'Sent Successfully',
|
title: 'Sent Successfully',
|
||||||
widgeta: Column(
|
widgeta: Column(
|
||||||
children: [
|
children: [
|
||||||
if (visitorBloc
|
if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
|
||||||
.passwordStatus!.failedOperations.isNotEmpty)
|
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
const Text('Failed Devices'),
|
const Text('Failed Devices'),
|
||||||
@ -52,8 +45,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: visitorBloc
|
itemCount: visitorBloc.passwordStatus!.failedOperations.length,
|
||||||
.passwordStatus!.failedOperations.length,
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.all(5),
|
margin: EdgeInsets.all(5),
|
||||||
@ -61,17 +53,14 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
height: 45,
|
height: 45,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(visitorBloc
|
child: Text(visitorBloc
|
||||||
.passwordStatus!
|
.passwordStatus!.failedOperations[index].deviceUuid)),
|
||||||
.failedOperations[index]
|
|
||||||
.deviceUuid)),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (visitorBloc
|
if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
|
||||||
.passwordStatus!.successOperations.isNotEmpty)
|
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
const Text('Success Devices'),
|
const Text('Success Devices'),
|
||||||
@ -81,18 +70,15 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: visitorBloc
|
itemCount: visitorBloc.passwordStatus!.successOperations.length,
|
||||||
.passwordStatus!.successOperations.length,
|
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.all(5),
|
margin: EdgeInsets.all(5),
|
||||||
decoration: containerDecoration,
|
decoration: containerDecoration,
|
||||||
height: 45,
|
height: 45,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(visitorBloc
|
child: Text(visitorBloc.passwordStatus!
|
||||||
.passwordStatus!
|
.successOperations[index].deviceUuid)),
|
||||||
.successOperations[index]
|
|
||||||
.deviceUuid)),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -103,6 +89,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
))
|
))
|
||||||
.then((v) {
|
.then((v) {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
|
|
||||||
});
|
});
|
||||||
} else if (state is FailedState) {
|
} else if (state is FailedState) {
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
@ -115,16 +102,15 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
|
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
|
||||||
builder: (BuildContext context, VisitorPasswordState state) {
|
builder: (BuildContext context, VisitorPasswordState state) {
|
||||||
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
|
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
|
||||||
bool isRepeat =
|
bool isRepeat = state is IsRepeatState ? state.repeat : visitorBloc.repeat;
|
||||||
state is IsRepeatState ? state.repeat : visitorBloc.repeat;
|
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
title: Text(
|
title: Text(
|
||||||
'Create visitor password',
|
'Create visitor password',
|
||||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
style: Theme.of(context)
|
||||||
fontWeight: FontWeight.w400,
|
.textTheme
|
||||||
fontSize: 24,
|
.headlineLarge!
|
||||||
color: Colors.black),
|
.copyWith(fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black),
|
||||||
),
|
),
|
||||||
content: state is LoadingInitialState
|
content: state is LoadingInitialState
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
@ -135,11 +121,34 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(5.0),
|
padding: const EdgeInsets.all(5.0),
|
||||||
child: ListBody(
|
child: ListBody(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
NameAndEmailFields(
|
Container(
|
||||||
nameController: visitorBloc.userNameController,
|
child: Row(
|
||||||
emailController: visitorBloc.emailController,
|
children: [
|
||||||
nameValidator: visitorBloc.validate,
|
Expanded(
|
||||||
emailValidator: visitorBloc.validateEmail,
|
flex: 2,
|
||||||
|
child: CustomWebTextField(
|
||||||
|
validator: visitorBloc.validate,
|
||||||
|
controller: visitorBloc.userNameController,
|
||||||
|
isRequired: true,
|
||||||
|
textFieldName: 'Name',
|
||||||
|
description: '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: CustomWebTextField(
|
||||||
|
validator: visitorBloc.validateEmail,
|
||||||
|
controller: visitorBloc.emailController,
|
||||||
|
isRequired: true,
|
||||||
|
textFieldName: 'Email Address',
|
||||||
|
description:
|
||||||
|
'The password will be sent to the visitor’s email address.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 15,
|
height: 15,
|
||||||
@ -147,43 +156,107 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
AccessTypeRadioGroup(
|
Row(
|
||||||
selectedType: state is PasswordTypeSelected
|
children: [
|
||||||
? state.selectedType
|
Text(
|
||||||
: null,
|
'* ',
|
||||||
accessTypeSelected:
|
style: Theme.of(context)
|
||||||
visitorBloc.accessTypeSelected,
|
.textTheme
|
||||||
onTypeSelected: (value) {
|
.bodyMedium!
|
||||||
context
|
.copyWith(color: Colors.red),
|
||||||
.read<VisitorPasswordBloc>()
|
),
|
||||||
.add(SelectPasswordType(value));
|
Text('Access Type', style: text),
|
||||||
},
|
],
|
||||||
visitorBloc: visitorBloc,
|
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
if (visitorBloc.accessTypeSelected ==
|
children: <Widget>[
|
||||||
'Online Password')
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: size.width * 0.12,
|
||||||
|
child: RadioListTile<String>(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(
|
||||||
|
'Online Password',
|
||||||
|
style: text,
|
||||||
|
),
|
||||||
|
value: 'Online Password',
|
||||||
|
groupValue: (state is PasswordTypeSelected)
|
||||||
|
? state.selectedType
|
||||||
|
: visitorBloc.accessTypeSelected,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
context
|
||||||
|
.read<VisitorPasswordBloc>()
|
||||||
|
.add(SelectPasswordType(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: size.width * 0.12,
|
||||||
|
child: RadioListTile<String>(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text('Offline Password', style: text),
|
||||||
|
value: 'Offline Password',
|
||||||
|
groupValue: (state is PasswordTypeSelected)
|
||||||
|
? state.selectedType
|
||||||
|
: visitorBloc.accessTypeSelected,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
context
|
||||||
|
.read<VisitorPasswordBloc>()
|
||||||
|
.add(SelectPasswordType(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// SizedBox(
|
||||||
|
// width: size.width * 0.12,
|
||||||
|
// child: RadioListTile<String>(
|
||||||
|
// contentPadding: EdgeInsets.zero,
|
||||||
|
// title: Text(
|
||||||
|
// 'Dynamic Password',
|
||||||
|
// style: text,
|
||||||
|
// ),
|
||||||
|
// value: 'Dynamic Password',
|
||||||
|
// groupValue: (state is PasswordTypeSelected)
|
||||||
|
// ? state.selectedType
|
||||||
|
// : visitorBloc.accessTypeSelected,
|
||||||
|
// onChanged: (String? value) {
|
||||||
|
// if (value != null) {
|
||||||
|
// context
|
||||||
|
// .read<VisitorPasswordBloc>()
|
||||||
|
// .add(SelectPasswordType(value));
|
||||||
|
// visitorBloc.usageFrequencySelected = '';
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
const Spacer(
|
||||||
|
flex: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (visitorBloc.accessTypeSelected == 'Online Password')
|
||||||
Text(
|
Text(
|
||||||
'Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password',
|
'Only currently online devices can be selected. It is recommended to use when the device network is stable, and the system randomly generates a digital password',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontWeight: FontWeight.w400,
|
||||||
.bodySmall!
|
color: ColorsManager.grayColor,
|
||||||
.copyWith(
|
fontSize: 9),
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
),
|
||||||
if (visitorBloc.accessTypeSelected ==
|
if (visitorBloc.accessTypeSelected == 'Offline Password')
|
||||||
'Offline Password')
|
|
||||||
Text(
|
Text(
|
||||||
'Unaffected by the online status of the device, you can select online or offline device, and the system randomly generates a digital password',
|
'Unaffected by the online status of the device, you can select online or offline device, and the system randomly generates a digital password',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontWeight: FontWeight.w400,
|
||||||
.bodySmall!
|
color: ColorsManager.grayColor,
|
||||||
.copyWith(
|
fontSize: 9),
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
),
|
||||||
// if (visitorBloc.accessTypeSelected == 'Dynamic Password')
|
// if (visitorBloc.accessTypeSelected == 'Dynamic Password')
|
||||||
// Text(
|
// Text(
|
||||||
@ -198,170 +271,143 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (visitorBloc.accessTypeSelected ==
|
visitorBloc.accessTypeSelected == 'Dynamic Password'
|
||||||
'Dynamic Password')
|
? const SizedBox()
|
||||||
const SizedBox()
|
: Column(
|
||||||
else
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
'* ',
|
children: [
|
||||||
style: Theme.of(context)
|
Text(
|
||||||
.textTheme
|
'* ',
|
||||||
.bodyMedium!
|
style: Theme.of(context)
|
||||||
.copyWith(color: Colors.red),
|
.textTheme
|
||||||
|
.bodyMedium!
|
||||||
|
.copyWith(color: Colors.red),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Usage Frequency',
|
||||||
|
style: text,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Row(
|
||||||
'Usage Frequency',
|
children: <Widget>[
|
||||||
style: text,
|
SizedBox(
|
||||||
|
width: size.width * 0.12,
|
||||||
|
child: RadioListTile<String>(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text(
|
||||||
|
'One-Time',
|
||||||
|
style: text,
|
||||||
|
),
|
||||||
|
value: 'One-Time',
|
||||||
|
groupValue: (state is UsageFrequencySelected)
|
||||||
|
? state.selectedFrequency
|
||||||
|
: visitorBloc.usageFrequencySelected,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
context
|
||||||
|
.read<VisitorPasswordBloc>()
|
||||||
|
.add(SelectUsageFrequency(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: size.width * 0.12,
|
||||||
|
child: RadioListTile<String>(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: Text('Periodic', style: text),
|
||||||
|
value: 'Periodic',
|
||||||
|
groupValue: (state is UsageFrequencySelected)
|
||||||
|
? state.selectedFrequency
|
||||||
|
: visitorBloc.usageFrequencySelected,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
context.read<VisitorPasswordBloc>()
|
||||||
|
.add(SelectUsageFrequency(value));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
//One-Time
|
||||||
|
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
||||||
|
visitorBloc.accessTypeSelected == 'Online Password')
|
||||||
|
Text(
|
||||||
|
'Within the validity period, each device can be unlocked only once.',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.grayColor, fontSize: 9),
|
||||||
|
),
|
||||||
|
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
||||||
|
visitorBloc.accessTypeSelected == 'Offline Password')
|
||||||
|
Text(
|
||||||
|
'Within the validity period, each device can be unlocked only once, and the maximum validity period is 6 hours',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.grayColor, fontSize: 9),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Periodic
|
||||||
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
|
visitorBloc.accessTypeSelected == 'Offline Password')
|
||||||
|
Text(
|
||||||
|
'Within the validity period, there is no limit to the number of times each device can be unlocked, and it should be used at least once within 24 hours after the entry into force.',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.grayColor, fontSize: 9),
|
||||||
|
),
|
||||||
|
|
||||||
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
|
visitorBloc.accessTypeSelected == 'Online Password')
|
||||||
|
Text(
|
||||||
|
'Within the validity period, there is no limit to the number of times each device can be unlocked.',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.grayColor, fontSize: 9),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
UsageFrequencyRadioGroup(
|
|
||||||
selectedFrequency:
|
|
||||||
state is UsageFrequencySelected
|
|
||||||
? state.selectedFrequency
|
|
||||||
: null,
|
|
||||||
usageFrequencySelected:
|
|
||||||
visitorBloc.usageFrequencySelected,
|
|
||||||
onFrequencySelected: (value) {
|
|
||||||
context
|
|
||||||
.read<VisitorPasswordBloc>()
|
|
||||||
.add(SelectUsageFrequency(value));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
|
|
||||||
//One-Time
|
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
|
||||||
'One-Time' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Online Password')
|
|
||||||
Text(
|
|
||||||
'Within the validity period, each device can be unlocked only once.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
|
||||||
'One-Time' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password')
|
|
||||||
Text(
|
|
||||||
'Within the validity period, each device can be unlocked only once, and the maximum validity period is 6 hours',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Periodic
|
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
|
||||||
'Periodic' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password')
|
|
||||||
Text(
|
|
||||||
'Within the validity period, there is no limit to the number of times each device can be unlocked, and it should be used at least once within 24 hours after the entry into force.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
|
||||||
|
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
|
||||||
'Periodic' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Online Password')
|
|
||||||
Text(
|
|
||||||
'Within the validity period, there is no limit to the number of times each device can be unlocked.',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
if ((visitorBloc.usageFrequencySelected !=
|
if ((visitorBloc.usageFrequencySelected != 'One-Time' ||
|
||||||
'One-Time' ||
|
visitorBloc.accessTypeSelected != 'Offline Password') &&
|
||||||
visitorBloc.accessTypeSelected !=
|
|
||||||
'Offline Password') &&
|
|
||||||
(visitorBloc.usageFrequencySelected != ''))
|
(visitorBloc.usageFrequencySelected != ''))
|
||||||
DateTimeWebWidget(
|
DateTimeWebWidget(
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
title: 'Access Period',
|
title: 'Access Period',
|
||||||
size: size,
|
size: size,
|
||||||
endTime: () {
|
endTime: () {
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
'Periodic' &&
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
visitorBloc.accessTypeSelected ==
|
visitorBloc.add(SelectTimeEvent(context: context, isEffective: false));
|
||||||
'Offline Password') {
|
|
||||||
visitorBloc.add(SelectTimeEvent(
|
|
||||||
context: context,
|
|
||||||
isEffective: false));
|
|
||||||
} else {
|
} else {
|
||||||
visitorBloc.add(
|
visitorBloc.add(SelectTimeVisitorPassword(context: context, isStart: false, isRepeat: false));
|
||||||
SelectTimeVisitorPassword(
|
|
||||||
context: context,
|
|
||||||
isStart: false,
|
|
||||||
isRepeat: false));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
startTime: () {
|
startTime: () {
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
'Periodic' &&
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password') {
|
|
||||||
visitorBloc.add(SelectTimeEvent(
|
|
||||||
context: context,
|
|
||||||
isEffective: true));
|
|
||||||
} else {
|
|
||||||
visitorBloc.add(
|
visitorBloc.add(
|
||||||
SelectTimeVisitorPassword(
|
SelectTimeEvent(context: context, isEffective: true));
|
||||||
context: context,
|
} else {
|
||||||
isStart: true,
|
visitorBloc.add(SelectTimeVisitorPassword(
|
||||||
isRepeat: false));
|
context: context, isStart: true, isRepeat: false));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
firstString: (visitorBloc
|
firstString: (visitorBloc.usageFrequencySelected ==
|
||||||
.usageFrequencySelected ==
|
'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password')
|
||||||
'Periodic' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password')
|
|
||||||
? visitorBloc.effectiveTime
|
? visitorBloc.effectiveTime
|
||||||
: visitorBloc.startTimeAccess
|
: visitorBloc.startTimeAccess.toString(),
|
||||||
.toString(),
|
secondString: (visitorBloc.usageFrequencySelected ==
|
||||||
secondString: (visitorBloc
|
'Periodic' && visitorBloc.accessTypeSelected == 'Offline Password')
|
||||||
.usageFrequencySelected ==
|
|
||||||
'Periodic' &&
|
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password')
|
|
||||||
? visitorBloc.expirationTime
|
? visitorBloc.expirationTime
|
||||||
: visitorBloc.endTimeAccess.toString(),
|
: visitorBloc.endTimeAccess.toString(),
|
||||||
icon: Assets.calendarIcon),
|
icon: Assets.calendarIcon),
|
||||||
const SizedBox(
|
const SizedBox(height: 10,),
|
||||||
height: 10,
|
Text(visitorBloc.accessPeriodValidate,
|
||||||
),
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: ColorsManager.red),),
|
||||||
Text(
|
|
||||||
visitorBloc.accessPeriodValidate,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: ColorsManager.red),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
@ -385,21 +431,16 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Within the validity period, each device can be unlocked only once.',
|
'Within the validity period, each device can be unlocked only once.',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontWeight: FontWeight.w400,
|
||||||
.bodySmall!
|
color: ColorsManager.grayColor,
|
||||||
.copyWith(
|
fontSize: 9),
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: ColorsManager.grayColor,
|
|
||||||
fontSize: 9),
|
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
),
|
),
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
'Periodic' &&
|
visitorBloc.accessTypeSelected == 'Online Password')
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Online Password')
|
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 100,
|
width: 100,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -410,8 +451,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: CupertinoSwitch(
|
child: CupertinoSwitch(
|
||||||
value: visitorBloc.repeat,
|
value: visitorBloc.repeat,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
visitorBloc
|
visitorBloc.add(ToggleRepeatEvent());
|
||||||
.add(ToggleRepeatEvent());
|
|
||||||
},
|
},
|
||||||
applyTheme: true,
|
applyTheme: true,
|
||||||
),
|
),
|
||||||
@ -419,16 +459,12 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
'Periodic' &&
|
visitorBloc.accessTypeSelected == 'Online Password')
|
||||||
visitorBloc.accessTypeSelected ==
|
isRepeat ? const RepeatWidget() : const SizedBox(),
|
||||||
'Online Password')
|
|
||||||
isRepeat
|
|
||||||
? const RepeatWidget()
|
|
||||||
: const SizedBox(),
|
|
||||||
Container(
|
Container(
|
||||||
decoration: containerDecoration,
|
decoration: containerDecoration,
|
||||||
width: size.width / 6,
|
width: size.width / 9,
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
@ -436,28 +472,22 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AddDeviceDialog(
|
return AddDeviceDialog(
|
||||||
selectedDeviceIds:
|
selectedDeviceIds: visitorBloc.selectedDevices,
|
||||||
visitorBloc.selectedDevices,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).then((listDevice) {
|
).then((listDevice) {
|
||||||
if (listDevice != null) {
|
if (listDevice != null) {
|
||||||
visitorBloc.selectedDevices =
|
visitorBloc.selectedDevices = listDevice;
|
||||||
listDevice;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
child: Text(
|
child: Text(
|
||||||
'+ Add Device',
|
'+ Add Device',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontWeight: FontWeight.w400,
|
||||||
.bodySmall!
|
color: ColorsManager.whiteColors,
|
||||||
.copyWith(
|
fontSize: 12),
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color:
|
|
||||||
ColorsManager.whiteColors,
|
|
||||||
fontSize: 12),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -495,37 +525,30 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (visitorBloc.forgetFormKey.currentState!.validate()) {
|
if (visitorBloc.forgetFormKey.currentState!.validate()) {
|
||||||
if (visitorBloc.selectedDevices.isNotEmpty) {
|
if (visitorBloc.selectedDevices.isNotEmpty) {
|
||||||
if (visitorBloc.usageFrequencySelected ==
|
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
||||||
'One-Time' &&
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password') {
|
|
||||||
setPasswordFunction(context, size, visitorBloc);
|
setPasswordFunction(context, size, visitorBloc);
|
||||||
} else if (visitorBloc.usageFrequencySelected ==
|
} else if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
'Periodic' &&
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
visitorBloc.accessTypeSelected ==
|
|
||||||
'Offline Password') {
|
|
||||||
if (visitorBloc.expirationTime != 'End Time' &&
|
if (visitorBloc.expirationTime != 'End Time' &&
|
||||||
visitorBloc.effectiveTime != 'Start Time') {
|
visitorBloc.effectiveTime != 'Start Time' ) {
|
||||||
setPasswordFunction(context, size, visitorBloc);
|
setPasswordFunction(context, size, visitorBloc);
|
||||||
} else {
|
}else{
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
context: context,
|
context: context,
|
||||||
message:
|
message: 'Please select Access Period to continue',
|
||||||
'Please select Access Period to continue',
|
|
||||||
title: 'Access Period');
|
title: 'Access Period');
|
||||||
}
|
}
|
||||||
} else if (visitorBloc.endTimeAccess.toString() !=
|
} else if(
|
||||||
'End Time' &&
|
visitorBloc.endTimeAccess.toString()!='End Time'
|
||||||
visitorBloc.startTimeAccess.toString() !=
|
&&visitorBloc.startTimeAccess.toString()!='Start Time') {
|
||||||
'Start Time') {
|
|
||||||
if (visitorBloc.effectiveTimeTimeStamp != null &&
|
if (visitorBloc.effectiveTimeTimeStamp != null &&
|
||||||
visitorBloc.expirationTimeTimeStamp != null) {
|
visitorBloc.expirationTimeTimeStamp != null) {
|
||||||
if (isRepeat == true) {
|
if (isRepeat == true) {
|
||||||
if (visitorBloc.expirationTime != 'End Time' &&
|
if (visitorBloc.expirationTime != 'End Time' &&
|
||||||
visitorBloc.effectiveTime != 'Start Time' &&
|
visitorBloc.effectiveTime != 'Start Time' &&
|
||||||
visitorBloc.selectedDays.isNotEmpty) {
|
visitorBloc.selectedDays.isNotEmpty) {
|
||||||
setPasswordFunction(
|
setPasswordFunction(context, size, visitorBloc);
|
||||||
context, size, visitorBloc);
|
|
||||||
} else {
|
} else {
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -539,16 +562,14 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
} else {
|
} else {
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
context: context,
|
context: context,
|
||||||
message:
|
message: 'Please select Access Period to continue',
|
||||||
'Please select Access Period to continue',
|
|
||||||
title: 'Access Period');
|
title: 'Access Period');
|
||||||
}
|
}
|
||||||
} else {
|
}else{
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
context: context,
|
context: context,
|
||||||
message:
|
message: 'Please select Access Period to continue',
|
||||||
'Please select Access Period to continue',
|
title: 'Access Period');
|
||||||
title: 'Access Period');
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
@ -594,8 +615,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: size.height * 0.25,
|
height: size.height * 0.25,
|
||||||
child: Center(
|
child: Center(
|
||||||
child:
|
child: CircularProgressIndicator(), // Display a loading spinner
|
||||||
CircularProgressIndicator(), // Display a loading spinner
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -619,10 +639,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Set Password',
|
'Set Password',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
||||||
.textTheme
|
|
||||||
.headlineLarge!
|
|
||||||
.copyWith(
|
|
||||||
fontSize: 30,
|
fontSize: 30,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
@ -672,45 +689,37 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
||||||
visitorBloc.accessTypeSelected ==
|
visitorBloc.accessTypeSelected == 'Online Password') {
|
||||||
'Online Password') {
|
|
||||||
visitorBloc.add(OnlineOneTimePasswordEvent(
|
visitorBloc.add(OnlineOneTimePasswordEvent(
|
||||||
context: context,
|
context: context,
|
||||||
passwordName: visitorBloc.userNameController.text,
|
passwordName: visitorBloc.userNameController.text,
|
||||||
email: visitorBloc.emailController.text,
|
email: visitorBloc.emailController.text,
|
||||||
));
|
));
|
||||||
} else if (visitorBloc.usageFrequencySelected ==
|
}
|
||||||
'Periodic' &&
|
else if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
visitorBloc.accessTypeSelected ==
|
visitorBloc.accessTypeSelected == 'Online Password') {
|
||||||
'Online Password') {
|
|
||||||
visitorBloc.add(OnlineMultipleTimePasswordEvent(
|
visitorBloc.add(OnlineMultipleTimePasswordEvent(
|
||||||
passwordName: visitorBloc.userNameController.text,
|
passwordName: visitorBloc.userNameController.text,
|
||||||
email: visitorBloc.emailController.text,
|
email: visitorBloc.emailController.text,
|
||||||
effectiveTime:
|
effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(),
|
||||||
visitorBloc.effectiveTimeTimeStamp.toString(),
|
invalidTime: visitorBloc.expirationTimeTimeStamp.toString(),
|
||||||
invalidTime:
|
|
||||||
visitorBloc.expirationTimeTimeStamp.toString(),
|
|
||||||
));
|
));
|
||||||
} else if (visitorBloc.usageFrequencySelected ==
|
}
|
||||||
'One-Time' &&
|
else if (visitorBloc.usageFrequencySelected == 'One-Time' &&
|
||||||
visitorBloc.accessTypeSelected ==
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
'Offline Password') {
|
|
||||||
visitorBloc.add(OfflineOneTimePasswordEvent(
|
visitorBloc.add(OfflineOneTimePasswordEvent(
|
||||||
context: context,
|
context: context,
|
||||||
passwordName: visitorBloc.userNameController.text,
|
passwordName: visitorBloc.userNameController.text,
|
||||||
email: visitorBloc.emailController.text,
|
email: visitorBloc.emailController.text,
|
||||||
));
|
));
|
||||||
} else if (visitorBloc.usageFrequencySelected ==
|
}
|
||||||
'Periodic' &&
|
else if (visitorBloc.usageFrequencySelected == 'Periodic' &&
|
||||||
visitorBloc.accessTypeSelected ==
|
visitorBloc.accessTypeSelected == 'Offline Password') {
|
||||||
'Offline Password') {
|
|
||||||
visitorBloc.add(OfflineMultipleTimePasswordEvent(
|
visitorBloc.add(OfflineMultipleTimePasswordEvent(
|
||||||
passwordName: visitorBloc.userNameController.text,
|
passwordName: visitorBloc.userNameController.text,
|
||||||
email: visitorBloc.emailController.text,
|
email: visitorBloc.emailController.text,
|
||||||
effectiveTime:
|
effectiveTime: visitorBloc.effectiveTimeTimeStamp.toString(),
|
||||||
visitorBloc.effectiveTimeTimeStamp.toString(),
|
invalidTime: visitorBloc.expirationTimeTimeStamp.toString(),
|
||||||
invalidTime:
|
|
||||||
visitorBloc.expirationTimeTimeStamp.toString(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user