mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-11-27 14:14:55 +00:00
Compare commits
17 Commits
SP-1474-FE
...
On-devices
| Author | SHA1 | Date | |
|---|---|---|---|
| 7750290be4 | |||
| 423ad6e687 | |||
| 932e50f518 | |||
| c649044a1f | |||
| c46cfb04a7 | |||
| 8754960713 | |||
| c6e98fa245 | |||
| 277a9ce4f0 | |||
| db9e856bca | |||
| 07435ec89e | |||
| 5a2299ea2f | |||
| 90f8305aa1 | |||
| 329b2ba472 | |||
| 0fb9149613 | |||
| 87b45fff1d | |||
| 95ae50d12d | |||
| 95d6e1ecda |
@ -50,6 +50,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
bool _selectAll = false;
|
bool _selectAll = false;
|
||||||
final ScrollController _verticalScrollController = ScrollController();
|
final ScrollController _verticalScrollController = ScrollController();
|
||||||
final ScrollController _horizontalScrollController = ScrollController();
|
final ScrollController _horizontalScrollController = ScrollController();
|
||||||
|
static const double _fixedRowHeight = 60;
|
||||||
|
static const double _checkboxColumnWidth = 50;
|
||||||
|
static const double _settingsColumnWidth = 100;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -67,7 +70,6 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
|
|
||||||
bool _compareListOfLists(
|
bool _compareListOfLists(
|
||||||
List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||||
// Check if the old and new lists are the same
|
|
||||||
if (oldList.length != newList.length) return false;
|
if (oldList.length != newList.length) return false;
|
||||||
|
|
||||||
for (int i = 0; i < oldList.length; i++) {
|
for (int i = 0; i < oldList.length; i++) {
|
||||||
@ -104,73 +106,130 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double get _totalTableWidth {
|
||||||
|
final hasSettings = widget.headers.contains('Settings');
|
||||||
|
final base = (widget.withCheckBox ? _checkboxColumnWidth : 0) +
|
||||||
|
(hasSettings ? _settingsColumnWidth : 0);
|
||||||
|
final regularCount = widget.headers.length - (hasSettings ? 1 : 0);
|
||||||
|
final regularWidth = (widget.size.width - base) / regularCount;
|
||||||
|
return base + regularCount * regularWidth;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
width: widget.size.width,
|
||||||
|
height: widget.size.height,
|
||||||
decoration: widget.cellDecoration,
|
decoration: widget.cellDecoration,
|
||||||
child: Scrollbar(
|
child: ScrollConfiguration(
|
||||||
controller: _verticalScrollController,
|
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||||
thumbVisibility: true,
|
|
||||||
trackVisibility: true,
|
|
||||||
child: Scrollbar(
|
child: Scrollbar(
|
||||||
//fixed the horizontal scrollbar issue
|
|
||||||
controller: _horizontalScrollController,
|
controller: _horizontalScrollController,
|
||||||
thumbVisibility: true,
|
thumbVisibility: true,
|
||||||
trackVisibility: true,
|
trackVisibility: true,
|
||||||
notificationPredicate: (notif) => notif.depth == 1,
|
notificationPredicate: (notif) =>
|
||||||
|
notif.metrics.axis == Axis.horizontal,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
controller: _verticalScrollController,
|
controller: _horizontalScrollController,
|
||||||
child: SingleChildScrollView(
|
scrollDirection: Axis.horizontal,
|
||||||
controller: _horizontalScrollController,
|
child: SizedBox(
|
||||||
scrollDirection: Axis.horizontal,
|
width: _totalTableWidth,
|
||||||
child: SizedBox(
|
child: Column(
|
||||||
width: widget.size.width,
|
children: [
|
||||||
child: Column(
|
Container(
|
||||||
children: [
|
height: _fixedRowHeight,
|
||||||
Container(
|
decoration: widget.headerDecoration ??
|
||||||
decoration: widget.headerDecoration ??
|
const BoxDecoration(color: ColorsManager.boxColor),
|
||||||
const BoxDecoration(
|
child: Row(
|
||||||
color: ColorsManager.boxColor,
|
children: [
|
||||||
|
if (widget.withCheckBox)
|
||||||
|
_buildSelectAllCheckbox(_checkboxColumnWidth),
|
||||||
|
for (var i = 0; i < widget.headers.length; i++)
|
||||||
|
_buildTableHeaderCell(
|
||||||
|
widget.headers[i],
|
||||||
|
widget.headers[i] == 'Settings'
|
||||||
|
? _settingsColumnWidth
|
||||||
|
: (_totalTableWidth -
|
||||||
|
(widget.withCheckBox
|
||||||
|
? _checkboxColumnWidth
|
||||||
|
: 0) -
|
||||||
|
(widget.headers.contains('Settings')
|
||||||
|
? _settingsColumnWidth
|
||||||
|
: 0)) /
|
||||||
|
(widget.headers.length -
|
||||||
|
(widget.headers.contains('Settings')
|
||||||
|
? 1
|
||||||
|
: 0)),
|
||||||
),
|
),
|
||||||
child: Row(
|
],
|
||||||
children: [
|
|
||||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
|
||||||
...List.generate(widget.headers.length, (index) {
|
|
||||||
return _buildTableHeaderCell(
|
|
||||||
widget.headers[index], index);
|
|
||||||
})
|
|
||||||
//...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
),
|
||||||
width: widget.size.width,
|
|
||||||
child: widget.isEmpty
|
Expanded(
|
||||||
? _buildEmptyState()
|
child: widget.isEmpty
|
||||||
: Column(
|
? _buildEmptyState()
|
||||||
children:
|
: Scrollbar(
|
||||||
List.generate(widget.data.length, (rowIndex) {
|
controller: _verticalScrollController,
|
||||||
|
thumbVisibility: true,
|
||||||
|
trackVisibility: true,
|
||||||
|
notificationPredicate: (notif) =>
|
||||||
|
notif.metrics.axis == Axis.vertical,
|
||||||
|
child: ListView.builder(
|
||||||
|
controller: _verticalScrollController,
|
||||||
|
itemCount: widget.data.length,
|
||||||
|
itemBuilder: (_, rowIndex) {
|
||||||
final row = widget.data[rowIndex];
|
final row = widget.data[rowIndex];
|
||||||
return Row(
|
return SizedBox(
|
||||||
children: [
|
height: _fixedRowHeight,
|
||||||
if (widget.withCheckBox)
|
child: Row(
|
||||||
_buildRowCheckbox(
|
children: [
|
||||||
rowIndex, widget.size.height * 0.08),
|
if (widget.withCheckBox)
|
||||||
...row.asMap().entries.map((entry) {
|
_buildRowCheckbox(
|
||||||
return _buildTableCell(
|
rowIndex,
|
||||||
entry.value.toString(),
|
_checkboxColumnWidth,
|
||||||
widget.size.height * 0.08,
|
),
|
||||||
rowIndex: rowIndex,
|
for (var colIndex = 0;
|
||||||
columnIndex: entry.key,
|
colIndex < row.length;
|
||||||
);
|
colIndex++)
|
||||||
}).toList(),
|
widget.headers[colIndex] == 'Settings'
|
||||||
],
|
? buildSettingsIcon(
|
||||||
|
width: _settingsColumnWidth,
|
||||||
|
onTap: () => widget
|
||||||
|
.onSettingsPressed
|
||||||
|
?.call(rowIndex),
|
||||||
|
)
|
||||||
|
: _buildTableCell(
|
||||||
|
row[colIndex].toString(),
|
||||||
|
width: widget.headers[
|
||||||
|
colIndex] ==
|
||||||
|
'Settings'
|
||||||
|
? _settingsColumnWidth
|
||||||
|
: (_totalTableWidth -
|
||||||
|
(widget.withCheckBox
|
||||||
|
? _checkboxColumnWidth
|
||||||
|
: 0) -
|
||||||
|
(widget.headers
|
||||||
|
.contains(
|
||||||
|
'Settings')
|
||||||
|
? _settingsColumnWidth
|
||||||
|
: 0)) /
|
||||||
|
(widget.headers.length -
|
||||||
|
(widget.headers
|
||||||
|
.contains(
|
||||||
|
'Settings')
|
||||||
|
? 1
|
||||||
|
: 0)),
|
||||||
|
rowIndex: rowIndex,
|
||||||
|
columnIndex: colIndex,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -210,9 +269,10 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Widget _buildSelectAllCheckbox() {
|
|
||||||
|
Widget _buildSelectAllCheckbox(double width) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 50,
|
width: width,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(color: ColorsManager.boxDivider),
|
vertical: BorderSide(color: ColorsManager.boxDivider),
|
||||||
@ -227,11 +287,11 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRowCheckbox(int index, double size) {
|
Widget _buildRowCheckbox(int index, double width) {
|
||||||
return Container(
|
return Container(
|
||||||
width: 50,
|
width: width,
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
height: size,
|
height: _fixedRowHeight,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
@ -253,50 +313,47 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTableHeaderCell(String title, int index) {
|
Widget _buildTableHeaderCell(String title, double width) {
|
||||||
return Expanded(
|
return Container(
|
||||||
child: Container(
|
width: width,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border.symmetric(
|
border: Border.symmetric(
|
||||||
vertical: BorderSide(color: ColorsManager.boxDivider),
|
vertical: BorderSide(color: ColorsManager.boxDivider),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints.expand(height: 40),
|
),
|
||||||
alignment: Alignment.centerLeft,
|
constraints: BoxConstraints(minHeight: 40, maxHeight: _fixedRowHeight),
|
||||||
child: Padding(
|
alignment: Alignment.centerLeft,
|
||||||
padding: EdgeInsets.symmetric(
|
child: Padding(
|
||||||
horizontal: index == widget.headers.length - 1 ? 12 : 8.0,
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4),
|
||||||
vertical: 4),
|
child: Text(
|
||||||
child: Text(
|
title,
|
||||||
title,
|
style: context.textTheme.titleSmall!.copyWith(
|
||||||
style: context.textTheme.titleSmall!.copyWith(
|
color: ColorsManager.grayColor,
|
||||||
color: ColorsManager.grayColor,
|
fontSize: 12,
|
||||||
fontSize: 12,
|
fontWeight: FontWeight.w400,
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTableCell(String content, double size,
|
Widget _buildTableCell(String content,
|
||||||
{required int rowIndex, required int columnIndex}) {
|
{required double width,
|
||||||
|
required int rowIndex,
|
||||||
|
required int columnIndex}) {
|
||||||
bool isBatteryLevel = content.endsWith('%');
|
bool isBatteryLevel = content.endsWith('%');
|
||||||
double? batteryLevel;
|
double? batteryLevel;
|
||||||
|
|
||||||
if (isBatteryLevel) {
|
if (isBatteryLevel) {
|
||||||
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSettingsColumn = widget.headers[columnIndex] == 'Settings';
|
bool isSettingsColumn = widget.headers[columnIndex] == 'Settings';
|
||||||
if (isSettingsColumn) {
|
if (isSettingsColumn) {
|
||||||
return buildSettingsIcon(
|
return buildSettingsIcon(
|
||||||
width: 120,
|
width: width, onTap: () => widget.onSettingsPressed?.call(rowIndex));
|
||||||
height: 60,
|
|
||||||
iconSize: 40,
|
|
||||||
onTap: () => widget.onSettingsPressed?.call(rowIndex),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color? statusColor;
|
Color? statusColor;
|
||||||
@ -320,92 +377,82 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
statusColor = Colors.black;
|
statusColor = Colors.black;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Expanded(
|
return Container(
|
||||||
child: Container(
|
width: width,
|
||||||
height: size,
|
height: _fixedRowHeight,
|
||||||
padding: const EdgeInsets.all(5.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
color: ColorsManager.boxDivider,
|
color: ColorsManager.boxDivider,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
alignment: Alignment.centerLeft,
|
color: Colors.white,
|
||||||
child: Text(
|
),
|
||||||
content,
|
alignment: Alignment.centerLeft,
|
||||||
style: TextStyle(
|
child: Text(
|
||||||
color: (batteryLevel != null && batteryLevel < 20)
|
content,
|
||||||
? ColorsManager.red
|
style: TextStyle(
|
||||||
: (batteryLevel != null && batteryLevel > 20)
|
color: (batteryLevel != null && batteryLevel < 20)
|
||||||
? ColorsManager.green
|
? ColorsManager.red
|
||||||
: statusColor,
|
: (batteryLevel != null && batteryLevel > 20)
|
||||||
fontSize: 13,
|
? ColorsManager.green
|
||||||
fontWeight: FontWeight.w400),
|
: statusColor,
|
||||||
maxLines: 2,
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildSettingsIcon(
|
Widget buildSettingsIcon({required double width, VoidCallback? onTap}) {
|
||||||
{double width = 120,
|
return Container(
|
||||||
double height = 60,
|
width: width,
|
||||||
double iconSize = 40,
|
height: _fixedRowHeight,
|
||||||
VoidCallback? onTap}) {
|
padding: const EdgeInsets.only(left: 15, top: 10, bottom: 10),
|
||||||
return Column(
|
decoration: const BoxDecoration(
|
||||||
children: [
|
color: ColorsManager.whiteColors,
|
||||||
Container(
|
border: Border(
|
||||||
padding: const EdgeInsets.only(top: 10, bottom: 15, left: 10),
|
bottom: BorderSide(
|
||||||
margin: const EdgeInsets.only(right: 15),
|
color: ColorsManager.boxDivider,
|
||||||
decoration: const BoxDecoration(
|
width: 1.0,
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
border: Border(
|
|
||||||
bottom: BorderSide(
|
|
||||||
color: ColorsManager.boxDivider,
|
|
||||||
width: 1.0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
width: width,
|
),
|
||||||
child: Padding(
|
),
|
||||||
padding: const EdgeInsets.only(
|
child: Align(
|
||||||
right: 16.0,
|
alignment: Alignment.centerLeft,
|
||||||
left: 17.0,
|
child: Container(
|
||||||
),
|
width: 50,
|
||||||
child: Container(
|
decoration: BoxDecoration(
|
||||||
width: 50,
|
color: const Color(0xFFF7F8FA),
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(20),
|
||||||
color: const Color(0xFFF7F8FA),
|
boxShadow: [
|
||||||
borderRadius: BorderRadius.circular(height / 2),
|
BoxShadow(
|
||||||
boxShadow: [
|
color: Colors.black.withOpacity(0.17),
|
||||||
BoxShadow(
|
blurRadius: 14,
|
||||||
color: Colors.black.withOpacity(0.17),
|
offset: const Offset(0, 4),
|
||||||
blurRadius: 14,
|
|
||||||
offset: const Offset(0, 4),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
child: InkWell(
|
],
|
||||||
onTap: onTap,
|
),
|
||||||
child: Padding(
|
child: InkWell(
|
||||||
padding: const EdgeInsets.all(8.0),
|
onTap: onTap,
|
||||||
child: Center(
|
child: Padding(
|
||||||
child: SvgPicture.asset(
|
padding: EdgeInsets.all(8.0),
|
||||||
Assets.settings,
|
child: Center(
|
||||||
width: 40,
|
child: SvgPicture.asset(
|
||||||
height: 22,
|
Assets.settings,
|
||||||
color: ColorsManager.primaryColor,
|
width: 40,
|
||||||
),
|
height: 20,
|
||||||
),
|
color: ColorsManager.primaryColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,8 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
|||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
//Smart Power Clamp
|
//Smart Power Clamp
|
||||||
class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
class SmartPowerDeviceControl extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
const SmartPowerDeviceControl({super.key, required this.deviceId});
|
||||||
@ -145,13 +146,16 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
|
|||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.arrow_left),
|
icon: const Icon(Icons.arrow_left),
|
||||||
onPressed: () {
|
onPressed: blocProvider.currentPage <= 0
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(-1));
|
? null
|
||||||
pageController.previousPage(
|
: () {
|
||||||
duration: const Duration(milliseconds: 300),
|
blocProvider
|
||||||
curve: Curves.easeInOut,
|
.add(SmartPowerArrowPressedEvent(-1));
|
||||||
);
|
pageController.previousPage(
|
||||||
},
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
currentPage == 0
|
currentPage == 0
|
||||||
@ -165,13 +169,16 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.arrow_right),
|
icon: const Icon(Icons.arrow_right),
|
||||||
onPressed: () {
|
onPressed: blocProvider.currentPage >= 3
|
||||||
blocProvider.add(SmartPowerArrowPressedEvent(1));
|
? null
|
||||||
pageController.nextPage(
|
: () {
|
||||||
duration: const Duration(milliseconds: 300),
|
blocProvider
|
||||||
curve: Curves.easeInOut,
|
.add(SmartPowerArrowPressedEvent(1));
|
||||||
);
|
pageController.nextPage(
|
||||||
},
|
duration: const Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -195,8 +202,8 @@ class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayou
|
|||||||
blocProvider.add(SelectDateEvent(context: context));
|
blocProvider.add(SelectDateEvent(context: context));
|
||||||
blocProvider.add(FilterRecordsByDateEvent(
|
blocProvider.add(FilterRecordsByDateEvent(
|
||||||
selectedDate: blocProvider.dateTime!,
|
selectedDate: blocProvider.dateTime!,
|
||||||
viewType:
|
viewType: blocProvider
|
||||||
blocProvider.views[blocProvider.currentIndex]));
|
.views[blocProvider.currentIndex]));
|
||||||
},
|
},
|
||||||
widget: blocProvider.dateSwitcher(),
|
widget: blocProvider.dateSwitcher(),
|
||||||
chartData: blocProvider.energyDataList.isNotEmpty
|
chartData: blocProvider.energyDataList.isNotEmpty
|
||||||
|
|||||||
@ -83,6 +83,12 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
emit(currentState.copyWith(
|
emit(currentState.copyWith(
|
||||||
scheduleMode: event.scheduleMode,
|
scheduleMode: event.scheduleMode,
|
||||||
countdownRemaining: Duration.zero,
|
countdownRemaining: Duration.zero,
|
||||||
|
countdownHours: 0,
|
||||||
|
countdownMinutes: 0,
|
||||||
|
inchingHours: 0,
|
||||||
|
inchingMinutes: 0,
|
||||||
|
isCountdownActive: false,
|
||||||
|
isInchingActive: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,6 +100,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
if (state is ScheduleLoaded) {
|
if (state is ScheduleLoaded) {
|
||||||
final currentState = state as ScheduleLoaded;
|
final currentState = state as ScheduleLoaded;
|
||||||
emit(currentState.copyWith(
|
emit(currentState.copyWith(
|
||||||
|
countdownSeconds: event.seconds,
|
||||||
countdownHours: event.hours,
|
countdownHours: event.hours,
|
||||||
countdownMinutes: event.minutes,
|
countdownMinutes: event.minutes,
|
||||||
inchingHours: 0,
|
inchingHours: 0,
|
||||||
@ -113,6 +120,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
inchingHours: event.hours,
|
inchingHours: event.hours,
|
||||||
inchingMinutes: event.minutes,
|
inchingMinutes: event.minutes,
|
||||||
countdownRemaining: Duration.zero,
|
countdownRemaining: Duration.zero,
|
||||||
|
inchingSeconds: 0, // Add this
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,6 +432,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
countdownMinutes: countdownDuration.inMinutes % 60,
|
countdownMinutes: countdownDuration.inMinutes % 60,
|
||||||
countdownRemaining: countdownDuration,
|
countdownRemaining: countdownDuration,
|
||||||
isCountdownActive: true,
|
isCountdownActive: true,
|
||||||
|
countdownSeconds: countdownDuration.inSeconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -437,6 +446,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
countdownMinutes: 0,
|
countdownMinutes: 0,
|
||||||
countdownRemaining: Duration.zero,
|
countdownRemaining: Duration.zero,
|
||||||
isCountdownActive: false,
|
isCountdownActive: false,
|
||||||
|
countdownSeconds: 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -448,6 +458,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
inchingMinutes: inchingDuration.inMinutes % 60,
|
inchingMinutes: inchingDuration.inMinutes % 60,
|
||||||
isInchingActive: true,
|
isInchingActive: true,
|
||||||
countdownRemaining: inchingDuration,
|
countdownRemaining: inchingDuration,
|
||||||
|
countdownSeconds: inchingDuration.inSeconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -574,8 +585,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String extractTime(String isoDateTime) {
|
String extractTime(String isoDateTime) {
|
||||||
// Example input: "2025-06-19T15:45:00.000"
|
return isoDateTime.split('T')[1].split('.')[0];
|
||||||
return isoDateTime.split('T')[1].split('.')[0]; // gives "15:45:00"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
int? getTimeStampWithoutSeconds(DateTime? dateTime) {
|
||||||
|
|||||||
@ -146,14 +146,16 @@ class UpdateScheduleModeEvent extends ScheduleEvent {
|
|||||||
class UpdateCountdownTimeEvent extends ScheduleEvent {
|
class UpdateCountdownTimeEvent extends ScheduleEvent {
|
||||||
final int hours;
|
final int hours;
|
||||||
final int minutes;
|
final int minutes;
|
||||||
|
final int seconds;
|
||||||
|
|
||||||
const UpdateCountdownTimeEvent({
|
const UpdateCountdownTimeEvent({
|
||||||
required this.hours,
|
required this.hours,
|
||||||
required this.minutes,
|
required this.minutes,
|
||||||
|
required this.seconds,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [hours, minutes];
|
List<Object> get props => [hours, minutes, seconds];
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateInchingTimeEvent extends ScheduleEvent {
|
class UpdateInchingTimeEvent extends ScheduleEvent {
|
||||||
|
|||||||
@ -26,11 +26,15 @@ class ScheduleLoaded extends ScheduleState {
|
|||||||
final bool isCountdownActive;
|
final bool isCountdownActive;
|
||||||
final int inchingHours;
|
final int inchingHours;
|
||||||
final int inchingMinutes;
|
final int inchingMinutes;
|
||||||
|
final int inchingSeconds;
|
||||||
final bool isInchingActive;
|
final bool isInchingActive;
|
||||||
final ScheduleModes scheduleMode;
|
final ScheduleModes scheduleMode;
|
||||||
final Duration? countdownRemaining;
|
final Duration? countdownRemaining;
|
||||||
|
final int? countdownSeconds;
|
||||||
|
|
||||||
const ScheduleLoaded({
|
const ScheduleLoaded({
|
||||||
|
this.countdownSeconds = 0,
|
||||||
|
this.inchingSeconds = 0,
|
||||||
required this.schedules,
|
required this.schedules,
|
||||||
this.selectedTime,
|
this.selectedTime,
|
||||||
required this.selectedDays,
|
required this.selectedDays,
|
||||||
@ -61,6 +65,9 @@ class ScheduleLoaded extends ScheduleState {
|
|||||||
bool? isInchingActive,
|
bool? isInchingActive,
|
||||||
ScheduleModes? scheduleMode,
|
ScheduleModes? scheduleMode,
|
||||||
Duration? countdownRemaining,
|
Duration? countdownRemaining,
|
||||||
|
String? deviceId,
|
||||||
|
int? countdownSeconds,
|
||||||
|
int? inchingSeconds,
|
||||||
}) {
|
}) {
|
||||||
return ScheduleLoaded(
|
return ScheduleLoaded(
|
||||||
schedules: schedules ?? this.schedules,
|
schedules: schedules ?? this.schedules,
|
||||||
@ -68,7 +75,7 @@ class ScheduleLoaded extends ScheduleState {
|
|||||||
selectedDays: selectedDays ?? this.selectedDays,
|
selectedDays: selectedDays ?? this.selectedDays,
|
||||||
functionOn: functionOn ?? this.functionOn,
|
functionOn: functionOn ?? this.functionOn,
|
||||||
isEditing: isEditing ?? this.isEditing,
|
isEditing: isEditing ?? this.isEditing,
|
||||||
deviceId: deviceId,
|
deviceId: deviceId ?? this.deviceId,
|
||||||
countdownHours: countdownHours ?? this.countdownHours,
|
countdownHours: countdownHours ?? this.countdownHours,
|
||||||
countdownMinutes: countdownMinutes ?? this.countdownMinutes,
|
countdownMinutes: countdownMinutes ?? this.countdownMinutes,
|
||||||
isCountdownActive: isCountdownActive ?? this.isCountdownActive,
|
isCountdownActive: isCountdownActive ?? this.isCountdownActive,
|
||||||
@ -77,6 +84,8 @@ class ScheduleLoaded extends ScheduleState {
|
|||||||
isInchingActive: isInchingActive ?? this.isInchingActive,
|
isInchingActive: isInchingActive ?? this.isInchingActive,
|
||||||
scheduleMode: scheduleMode ?? this.scheduleMode,
|
scheduleMode: scheduleMode ?? this.scheduleMode,
|
||||||
countdownRemaining: countdownRemaining ?? this.countdownRemaining,
|
countdownRemaining: countdownRemaining ?? this.countdownRemaining,
|
||||||
|
countdownSeconds: countdownSeconds ?? this.countdownSeconds,
|
||||||
|
inchingSeconds: inchingSeconds ?? this.inchingSeconds,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +105,8 @@ class ScheduleLoaded extends ScheduleState {
|
|||||||
isInchingActive,
|
isInchingActive,
|
||||||
scheduleMode,
|
scheduleMode,
|
||||||
countdownRemaining,
|
countdownRemaining,
|
||||||
|
countdownSeconds,
|
||||||
|
inchingSeconds,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,8 @@ 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 CountdownInchingView extends StatefulWidget {
|
class CountdownInchingView extends StatefulWidget {
|
||||||
const CountdownInchingView({super.key});
|
final String deviceId;
|
||||||
|
const CountdownInchingView({super.key, required this.deviceId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CountdownInchingView> createState() => _CountdownInchingViewState();
|
State<CountdownInchingView> createState() => _CountdownInchingViewState();
|
||||||
@ -15,25 +16,30 @@ class CountdownInchingView extends StatefulWidget {
|
|||||||
class _CountdownInchingViewState extends State<CountdownInchingView> {
|
class _CountdownInchingViewState extends State<CountdownInchingView> {
|
||||||
late FixedExtentScrollController _hoursController;
|
late FixedExtentScrollController _hoursController;
|
||||||
late FixedExtentScrollController _minutesController;
|
late FixedExtentScrollController _minutesController;
|
||||||
|
late FixedExtentScrollController _secondsController;
|
||||||
|
|
||||||
int _lastHours = -1;
|
int _lastHours = -1;
|
||||||
int _lastMinutes = -1;
|
int _lastMinutes = -1;
|
||||||
|
int _lastSeconds = -1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_hoursController = FixedExtentScrollController();
|
_hoursController = FixedExtentScrollController();
|
||||||
_minutesController = FixedExtentScrollController();
|
_minutesController = FixedExtentScrollController();
|
||||||
|
_secondsController = FixedExtentScrollController();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_hoursController.dispose();
|
_hoursController.dispose();
|
||||||
_minutesController.dispose();
|
_minutesController.dispose();
|
||||||
|
_secondsController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateControllers(int displayHours, int displayMinutes) {
|
void _updateControllers(
|
||||||
|
int displayHours, int displayMinutes, int displaySeconds) {
|
||||||
if (_lastHours != displayHours) {
|
if (_lastHours != displayHours) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (_hoursController.hasClients) {
|
if (_hoursController.hasClients) {
|
||||||
@ -50,6 +56,15 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
});
|
});
|
||||||
_lastMinutes = displayMinutes;
|
_lastMinutes = displayMinutes;
|
||||||
}
|
}
|
||||||
|
// Update seconds controller
|
||||||
|
if (_lastSeconds != displaySeconds) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (_secondsController.hasClients) {
|
||||||
|
_secondsController.jumpToItem(displaySeconds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_lastSeconds = displaySeconds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -57,7 +72,6 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
return BlocBuilder<ScheduleBloc, ScheduleState>(
|
return BlocBuilder<ScheduleBloc, ScheduleState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is! ScheduleLoaded) return const SizedBox.shrink();
|
if (state is! ScheduleLoaded) return const SizedBox.shrink();
|
||||||
|
|
||||||
final isCountDown = state.scheduleMode == ScheduleModes.countdown;
|
final isCountDown = state.scheduleMode == ScheduleModes.countdown;
|
||||||
final isActive =
|
final isActive =
|
||||||
isCountDown ? state.isCountdownActive : state.isInchingActive;
|
isCountDown ? state.isCountdownActive : state.isInchingActive;
|
||||||
@ -67,8 +81,21 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
final displayMinutes = isActive && state.countdownRemaining != null
|
final displayMinutes = isActive && state.countdownRemaining != null
|
||||||
? state.countdownRemaining!.inMinutes.remainder(60)
|
? state.countdownRemaining!.inMinutes.remainder(60)
|
||||||
: (isCountDown ? state.countdownMinutes : state.inchingMinutes);
|
: (isCountDown ? state.countdownMinutes : state.inchingMinutes);
|
||||||
|
final displaySeconds = isActive && state.countdownRemaining != null
|
||||||
|
? state.countdownRemaining!.inSeconds.remainder(60)
|
||||||
|
: (isCountDown ? state.countdownSeconds : state.inchingSeconds);
|
||||||
|
|
||||||
|
_updateControllers(displayHours, displayMinutes, displaySeconds!);
|
||||||
|
|
||||||
|
if (displayHours == 0 && displayMinutes == 0 && displaySeconds == 0) {
|
||||||
|
context.read<ScheduleBloc>().add(
|
||||||
|
StopScheduleEvent(
|
||||||
|
mode: ScheduleModes.countdown,
|
||||||
|
deviceId: widget.deviceId,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_updateControllers(displayHours, displayMinutes);
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -100,7 +127,10 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
(value) {
|
(value) {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
context.read<ScheduleBloc>().add(UpdateCountdownTimeEvent(
|
context.read<ScheduleBloc>().add(UpdateCountdownTimeEvent(
|
||||||
hours: value, minutes: displayMinutes));
|
hours: value,
|
||||||
|
minutes: displayMinutes,
|
||||||
|
seconds: displaySeconds,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isActive: isActive,
|
isActive: isActive,
|
||||||
@ -115,11 +145,35 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
|
|||||||
(value) {
|
(value) {
|
||||||
if (!isActive) {
|
if (!isActive) {
|
||||||
context.read<ScheduleBloc>().add(UpdateCountdownTimeEvent(
|
context.read<ScheduleBloc>().add(UpdateCountdownTimeEvent(
|
||||||
hours: displayHours, minutes: value));
|
hours: displayHours,
|
||||||
|
minutes: value,
|
||||||
|
seconds: displaySeconds,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isActive: isActive,
|
isActive: isActive,
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
if (isActive)
|
||||||
|
_buildPickerColumn(
|
||||||
|
context,
|
||||||
|
's',
|
||||||
|
displaySeconds,
|
||||||
|
60,
|
||||||
|
_secondsController,
|
||||||
|
(value) {
|
||||||
|
if (!isActive) {
|
||||||
|
context
|
||||||
|
.read<ScheduleBloc>()
|
||||||
|
.add(UpdateCountdownTimeEvent(
|
||||||
|
hours: displayHours,
|
||||||
|
minutes: displayMinutes,
|
||||||
|
seconds: value,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isActive: isActive,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -74,7 +74,9 @@ class BuildScheduleView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
if (state.scheduleMode == ScheduleModes.countdown ||
|
if (state.scheduleMode == ScheduleModes.countdown ||
|
||||||
state.scheduleMode == ScheduleModes.inching)
|
state.scheduleMode == ScheduleModes.inching)
|
||||||
const CountdownInchingView(),
|
CountdownInchingView(
|
||||||
|
deviceId: deviceUuid,
|
||||||
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (state.scheduleMode == ScheduleModes.countdown)
|
if (state.scheduleMode == ScheduleModes.countdown)
|
||||||
CountdownModeButtons(
|
CountdownModeButtons(
|
||||||
|
|||||||
@ -79,6 +79,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDeviceInfoSection() {
|
Widget _buildDeviceInfoSection() {
|
||||||
|
final isOnlineDevice = device.online != null && device.online!;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 50),
|
padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 50),
|
||||||
child: Table(
|
child: Table(
|
||||||
@ -107,7 +108,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
'Installation Date and Time:',
|
'Installation Date and Time:',
|
||||||
formatDateTime(
|
formatDateTime(
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
((device.createTime ?? 0) * 1000),
|
(device.createTime ?? 0) * 1000,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -126,12 +127,16 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Status:', 'Online', statusColor: Colors.green),
|
_buildInfoRow(
|
||||||
|
'Status:',
|
||||||
|
isOnlineDevice ? 'Online' : 'offline',
|
||||||
|
statusColor: isOnlineDevice ? Colors.green : Colors.red,
|
||||||
|
),
|
||||||
_buildInfoRow(
|
_buildInfoRow(
|
||||||
'Last Offline Date and Time:',
|
'Last Offline Date and Time:',
|
||||||
formatDateTime(
|
formatDateTime(
|
||||||
DateTime.fromMillisecondsSinceEpoch(
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
((device.updateTime ?? 0) * 1000),
|
(device.updateTime ?? 0) * 1000,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -7,21 +7,22 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
final Map<String, Offset> positions;
|
final Map<String, Offset> positions;
|
||||||
final double cardWidth = 150.0;
|
final double cardWidth = 150.0;
|
||||||
final double cardHeight = 90.0;
|
final double cardHeight = 90.0;
|
||||||
final String? selectedSpaceUuid;
|
final Set<String> highlightedUuids;
|
||||||
|
|
||||||
SpacesConnectionsArrowPainter({
|
SpacesConnectionsArrowPainter({
|
||||||
required this.connections,
|
required this.connections,
|
||||||
required this.positions,
|
required this.positions,
|
||||||
this.selectedSpaceUuid,
|
required this.highlightedUuids,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
for (final connection in connections) {
|
for (final connection in connections) {
|
||||||
final isSelected = connection.to == selectedSpaceUuid;
|
final isSelected = highlightedUuids.contains(connection.from) ||
|
||||||
|
highlightedUuids.contains(connection.to);
|
||||||
final paint = Paint()
|
final paint = Paint()
|
||||||
..color = isSelected
|
..color = isSelected
|
||||||
? ColorsManager.primaryColor
|
? ColorsManager.blackColor
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||||
..strokeWidth = 2.0
|
..strokeWidth = 2.0
|
||||||
..style = PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
@ -36,7 +37,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
|
|
||||||
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
final path = Path()..moveTo(startPoint.dx, startPoint.dy);
|
||||||
|
|
||||||
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 60);
|
final controlPoint1 = Offset(startPoint.dx, startPoint.dy + 20);
|
||||||
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60);
|
final controlPoint2 = Offset(endPoint.dx, endPoint.dy - 60);
|
||||||
|
|
||||||
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
path.cubicTo(controlPoint1.dx, controlPoint1.dy, controlPoint2.dx,
|
||||||
@ -46,7 +47,7 @@ class SpacesConnectionsArrowPainter extends CustomPainter {
|
|||||||
|
|
||||||
final circlePaint = Paint()
|
final circlePaint = Paint()
|
||||||
..color = isSelected
|
..color = isSelected
|
||||||
? ColorsManager.primaryColor
|
? ColorsManager.blackColor
|
||||||
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
: ColorsManager.blackColor.withValues(alpha: 0.5)
|
||||||
..style = PaintingStyle.fill
|
..style = PaintingStyle.fill
|
||||||
..blendMode = BlendMode.srcIn;
|
..blendMode = BlendMode.srcIn;
|
||||||
|
|||||||
@ -1,21 +1,26 @@
|
|||||||
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/models/space_connection_model.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/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_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/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/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/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
|
|
||||||
class CommunityStructureCanvas extends StatefulWidget {
|
class CommunityStructureCanvas extends StatefulWidget {
|
||||||
const CommunityStructureCanvas({
|
const CommunityStructureCanvas({
|
||||||
required this.community,
|
required this.community,
|
||||||
|
required this.selectedSpace,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final CommunityModel community;
|
final CommunityModel community;
|
||||||
|
final SpaceModel? selectedSpace;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CommunityStructureCanvas> createState() =>_CommunityStructureCanvasState();
|
State<CommunityStructureCanvas> createState() => _CommunityStructureCanvasState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
||||||
@ -25,19 +30,30 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
final double _cardHeight = 90.0;
|
final double _cardHeight = 90.0;
|
||||||
final double _horizontalSpacing = 150.0;
|
final double _horizontalSpacing = 150.0;
|
||||||
final double _verticalSpacing = 120.0;
|
final double _verticalSpacing = 120.0;
|
||||||
String? _selectedSpaceUuid;
|
|
||||||
|
|
||||||
late TransformationController _transformationController;
|
late TransformationController _transformationController;
|
||||||
late AnimationController _animationController;
|
late AnimationController _animationController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
|
||||||
_transformationController = TransformationController();
|
_transformationController = TransformationController();
|
||||||
_animationController = AnimationController(
|
_animationController = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 150),
|
||||||
);
|
);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(covariant CommunityStructureCanvas oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (widget.selectedSpace?.uuid != oldWidget.selectedSpace?.uuid) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
if (mounted) {
|
||||||
|
_animateToSpace(widget.selectedSpace);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -47,6 +63,15 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<String> _getAllDescendantUuids(SpaceModel space) {
|
||||||
|
final uuids = <String>{};
|
||||||
|
for (final child in space.children) {
|
||||||
|
uuids.add(child.uuid);
|
||||||
|
uuids.addAll(_getAllDescendantUuids(child));
|
||||||
|
}
|
||||||
|
return uuids;
|
||||||
|
}
|
||||||
|
|
||||||
void _runAnimation(Matrix4 target) {
|
void _runAnimation(Matrix4 target) {
|
||||||
final animation = Matrix4Tween(
|
final animation = Matrix4Tween(
|
||||||
begin: _transformationController.value,
|
begin: _transformationController.value,
|
||||||
@ -63,15 +88,16 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onSpaceTapped(String spaceUuid) {
|
void _animateToSpace(SpaceModel? space) {
|
||||||
setState(() {
|
if (space == null) {
|
||||||
_selectedSpaceUuid = spaceUuid;
|
_runAnimation(Matrix4.identity());
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final position = _positions[spaceUuid];
|
final position = _positions[space.uuid];
|
||||||
if (position == null) return;
|
if (position == null) return;
|
||||||
|
|
||||||
const scale = 2.0;
|
const scale = 1.5;
|
||||||
final viewSize = context.size;
|
final viewSize = context.size;
|
||||||
if (viewSize == null) return;
|
if (viewSize == null) return;
|
||||||
|
|
||||||
@ -86,11 +112,19 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
_runAnimation(matrix);
|
_runAnimation(matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onSpaceTapped(SpaceModel? space) {
|
||||||
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
|
SelectSpaceEvent(community: widget.community, space: space),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _resetSelectionAndZoom() {
|
void _resetSelectionAndZoom() {
|
||||||
setState(() {
|
context.read<CommunitiesTreeSelectionBloc>().add(
|
||||||
_selectedSpaceUuid = null;
|
SelectSpaceEvent(
|
||||||
});
|
community: widget.community,
|
||||||
_runAnimation(Matrix4.identity());
|
space: null,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _calculateLayout(
|
void _calculateLayout(
|
||||||
@ -150,16 +184,23 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
|
|
||||||
_calculateLayout(community.spaces, 0, {});
|
_calculateLayout(community.spaces, 0, {});
|
||||||
|
|
||||||
|
final selectedSpace = widget.selectedSpace;
|
||||||
|
final highlightedUuids = <String>{};
|
||||||
|
if (selectedSpace != null) {
|
||||||
|
highlightedUuids.add(selectedSpace.uuid);
|
||||||
|
highlightedUuids.addAll(_getAllDescendantUuids(selectedSpace));
|
||||||
|
}
|
||||||
|
|
||||||
final widgets = <Widget>[];
|
final widgets = <Widget>[];
|
||||||
final connections = <SpaceConnectionModel>[];
|
final connections = <SpaceConnectionModel>[];
|
||||||
_generateWidgets(community.spaces, widgets, connections);
|
_generateWidgets(community.spaces, widgets, connections, highlightedUuids);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
CustomPaint(
|
CustomPaint(
|
||||||
painter: SpacesConnectionsArrowPainter(
|
painter: SpacesConnectionsArrowPainter(
|
||||||
connections: connections,
|
connections: connections,
|
||||||
positions: _positions,
|
positions: _positions,
|
||||||
selectedSpaceUuid: _selectedSpaceUuid,
|
highlightedUuids: highlightedUuids,
|
||||||
),
|
),
|
||||||
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
child: Stack(alignment: AlignmentDirectional.center, children: widgets),
|
||||||
),
|
),
|
||||||
@ -170,11 +211,15 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
List<SpaceModel> spaces,
|
List<SpaceModel> spaces,
|
||||||
List<Widget> widgets,
|
List<Widget> widgets,
|
||||||
List<SpaceConnectionModel> connections,
|
List<SpaceConnectionModel> connections,
|
||||||
|
Set<String> highlightedUuids,
|
||||||
) {
|
) {
|
||||||
for (final space in spaces) {
|
for (final space in spaces) {
|
||||||
final position = _positions[space.uuid];
|
final position = _positions[space.uuid];
|
||||||
if (position == null) continue;
|
if (position == null) continue;
|
||||||
|
|
||||||
|
final isHighlighted = highlightedUuids.contains(space.uuid);
|
||||||
|
final hasNoSelectedSpace = widget.selectedSpace == null;
|
||||||
|
|
||||||
widgets.add(
|
widgets.add(
|
||||||
Positioned(
|
Positioned(
|
||||||
left: position.dx,
|
left: position.dx,
|
||||||
@ -182,32 +227,31 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
width: _cardWidth,
|
width: _cardWidth,
|
||||||
height: _cardHeight,
|
height: _cardHeight,
|
||||||
child: SpaceCardWidget(
|
child: SpaceCardWidget(
|
||||||
index: spaces.indexOf(space),
|
buildSpaceContainer: () {
|
||||||
onPositionChanged: (newPosition) {},
|
|
||||||
buildSpaceContainer: (index) {
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: 1.0,
|
opacity: hasNoSelectedSpace || isHighlighted ? 1.0 : 0.5,
|
||||||
child: SpaceCell(
|
child: Tooltip(
|
||||||
index: index,
|
message: space.spaceName,
|
||||||
onTap: () => _onSpaceTapped(space.uuid),
|
preferBelow: false,
|
||||||
icon: space.icon,
|
child: SpaceCell(
|
||||||
name: space.spaceName,
|
onTap: () => _onSpaceTapped(space),
|
||||||
|
icon: space.icon,
|
||||||
|
name: space.spaceName,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
screenSize: MediaQuery.sizeOf(context),
|
onTap: () => SpaceDetailsDialogHelper.showCreate(context),
|
||||||
position: position,
|
|
||||||
isHovered: false,
|
|
||||||
onHoverChanged: (int index, bool isHovered) {},
|
|
||||||
onButtonTap: (int index, Offset newPosition) {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
for (final child in space.children) {
|
for (final child in space.children) {
|
||||||
connections.add(SpaceConnectionModel(from: space.uuid, to: child.uuid));
|
connections.add(
|
||||||
|
SpaceConnectionModel(from: space.uuid, to: child.uuid),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_generateWidgets(space.children, widgets, connections);
|
_generateWidgets(space.children, widgets, connections, highlightedUuids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +262,7 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
transformationController: _transformationController,
|
transformationController: _transformationController,
|
||||||
boundaryMargin: EdgeInsets.symmetric(
|
boundaryMargin: EdgeInsets.symmetric(
|
||||||
horizontal: MediaQuery.sizeOf(context).width * 0.3,
|
horizontal: MediaQuery.sizeOf(context).width * 0.3,
|
||||||
vertical: MediaQuery.sizeOf(context).height * 0.2,
|
vertical: MediaQuery.sizeOf(context).height * 0.3,
|
||||||
),
|
),
|
||||||
minScale: 0.5,
|
minScale: 0.5,
|
||||||
maxScale: 3.0,
|
maxScale: 3.0,
|
||||||
@ -226,8 +270,8 @@ class _CommunityStructureCanvasState extends State<CommunityStructureCanvas>
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: _resetSelectionAndZoom,
|
onTap: _resetSelectionAndZoom,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: MediaQuery.sizeOf(context).width * 2,
|
width: MediaQuery.sizeOf(context).width * 5,
|
||||||
height: MediaQuery.sizeOf(context).height * 2,
|
height: MediaQuery.sizeOf(context).height * 5,
|
||||||
child: Stack(children: treeWidgets),
|
child: Stack(children: treeWidgets),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class CreateSpaceButton extends StatelessWidget {
|
class CreateSpaceButton extends StatelessWidget {
|
||||||
@ -7,7 +8,7 @@ class CreateSpaceButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {},
|
onTap: () => SpaceDetailsDialogHelper.showCreate(context),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 60,
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
|||||||
@ -2,15 +2,11 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class PlusButtonWidget extends StatelessWidget {
|
class PlusButtonWidget extends StatelessWidget {
|
||||||
final int index;
|
|
||||||
final String direction;
|
|
||||||
final Offset offset;
|
final Offset offset;
|
||||||
final void Function(int index, Offset newPosition) onButtonTap;
|
final void Function() onButtonTap;
|
||||||
|
|
||||||
const PlusButtonWidget({
|
const PlusButtonWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.index,
|
|
||||||
required this.direction,
|
|
||||||
required this.offset,
|
required this.offset,
|
||||||
required this.onButtonTap,
|
required this.onButtonTap,
|
||||||
});
|
});
|
||||||
@ -18,13 +14,7 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: onButtonTap,
|
||||||
if (direction == 'down') {
|
|
||||||
onButtonTap(index, const Offset(0, 150));
|
|
||||||
} else {
|
|
||||||
onButtonTap(index, const Offset(150, 0));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 30,
|
width: 30,
|
||||||
height: 30,
|
height: 30,
|
||||||
|
|||||||
@ -1,60 +1,39 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/plus_button_widget.dart';
|
import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/plus_button_widget.dart';
|
||||||
|
|
||||||
class SpaceCardWidget extends StatelessWidget {
|
class SpaceCardWidget extends StatefulWidget {
|
||||||
final int index;
|
final void Function() onTap;
|
||||||
final Size screenSize;
|
final Widget Function() buildSpaceContainer;
|
||||||
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({
|
const SpaceCardWidget({
|
||||||
super.key,
|
required this.onTap,
|
||||||
required this.index,
|
|
||||||
required this.onPositionChanged,
|
|
||||||
required this.screenSize,
|
|
||||||
required this.position,
|
|
||||||
required this.isHovered,
|
|
||||||
required this.onHoverChanged,
|
|
||||||
required this.onButtonTap,
|
|
||||||
required this.buildSpaceContainer,
|
required this.buildSpaceContainer,
|
||||||
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SpaceCardWidget> createState() => _SpaceCardWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SpaceCardWidgetState extends State<SpaceCardWidget> {
|
||||||
|
bool isHovered = false;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (_) => onHoverChanged(index, true),
|
onEnter: (_) => setState(() => isHovered = true),
|
||||||
onExit: (_) => onHoverChanged(index, false),
|
onExit: (_) => setState(() => isHovered = false),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 150,
|
|
||||||
height: 90,
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip.none,
|
clipBehavior: Clip.none,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
buildSpaceContainer(index),
|
widget.buildSpaceContainer(),
|
||||||
|
|
||||||
if (isHovered)
|
if (isHovered)
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: PlusButtonWidget(
|
child: PlusButtonWidget(
|
||||||
index: index,
|
|
||||||
direction: 'down',
|
|
||||||
offset: Offset.zero,
|
offset: Offset.zero,
|
||||||
onButtonTap: onButtonTap,
|
onButtonTap: widget.onTap,
|
||||||
),
|
|
||||||
),
|
|
||||||
if (isHovered)
|
|
||||||
Positioned(
|
|
||||||
right: -15,
|
|
||||||
child: PlusButtonWidget(
|
|
||||||
index: index,
|
|
||||||
direction: 'right',
|
|
||||||
offset: Offset.zero,
|
|
||||||
onButtonTap: onButtonTap,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,29 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.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';
|
||||||
|
|
||||||
class SpaceCell extends StatelessWidget {
|
class SpaceCell extends StatelessWidget {
|
||||||
final int index;
|
|
||||||
final String icon;
|
final String icon;
|
||||||
final String name;
|
final String name;
|
||||||
final VoidCallback? onDoubleTap;
|
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
const SpaceCell({
|
const SpaceCell({
|
||||||
super.key,
|
super.key,
|
||||||
required this.index,
|
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.name,
|
required this.name,
|
||||||
this.onTap,
|
required this.onTap,
|
||||||
this.onDoubleTap,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Theme.of(context);
|
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onDoubleTap: onDoubleTap,
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 150,
|
width: 150,
|
||||||
@ -36,7 +30,7 @@ class SpaceCell extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
name,
|
name,
|
||||||
style: theme.textTheme.bodyLarge?.copyWith(
|
style: context.textTheme.bodyLarge?.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
@ -63,7 +57,10 @@ class SpaceCell extends StatelessWidget {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
icon,
|
icon,
|
||||||
color: ColorsManager.whiteColors,
|
colorFilter: const ColorFilter.mode(
|
||||||
|
ColorsManager.whiteColors,
|
||||||
|
BlendMode.srcIn,
|
||||||
|
),
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -9,14 +9,19 @@ class SpaceManagementCommunityStructure extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectedCommunity =
|
final selectionBloc = context.watch<CommunitiesTreeSelectionBloc>().state;
|
||||||
context.watch<CommunitiesTreeSelectionBloc>().state.selectedCommunity!;
|
final selectedCommunity = selectionBloc.selectedCommunity;
|
||||||
|
final selectedSpace = selectionBloc.selectedSpace;
|
||||||
const spacer = Spacer(flex: 10);
|
const spacer = Spacer(flex: 10);
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: selectedCommunity.spaces.isNotEmpty,
|
visible: selectedCommunity!.spaces.isNotEmpty,
|
||||||
replacement: const Row(
|
replacement: const Row(
|
||||||
children: [spacer, Expanded(child: CreateSpaceButton()), spacer]),
|
children: [spacer, Expanded(child: CreateSpaceButton()), spacer],
|
||||||
child: CommunityStructureCanvas(community: selectedCommunity),
|
),
|
||||||
|
child: CommunityStructureCanvas(
|
||||||
|
community: selectedCommunity,
|
||||||
|
selectedSpace: selectedSpace,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,26 +7,24 @@ class SpaceManagementTemplatesView extends StatelessWidget {
|
|||||||
const SpaceManagementTemplatesView({super.key});
|
const SpaceManagementTemplatesView({super.key});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Expanded(
|
return ColoredBox(
|
||||||
child: ColoredBox(
|
color: ColorsManager.whiteColors,
|
||||||
color: ColorsManager.whiteColors,
|
child: GridView.builder(
|
||||||
child: GridView.builder(
|
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
maxCrossAxisExtent: 400,
|
||||||
maxCrossAxisExtent: 400,
|
mainAxisSpacing: 10,
|
||||||
mainAxisSpacing: 10,
|
crossAxisSpacing: 10,
|
||||||
crossAxisSpacing: 10,
|
childAspectRatio: 2.0,
|
||||||
childAspectRatio: 2.0,
|
|
||||||
),
|
|
||||||
itemCount: _gridItems(context).length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final model = _gridItems(context)[index];
|
|
||||||
return CommunityTemplateCell(
|
|
||||||
onTap: model.onTap,
|
|
||||||
title: model.title,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
itemCount: _gridItems(context).length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final model = _gridItems(context)[index];
|
||||||
|
return CommunityTemplateCell(
|
||||||
|
onTap: model.onTap,
|
||||||
|
title: model.title,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ final class SelectCommunityEvent extends CommunitiesTreeSelectionEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
final class SelectSpaceEvent extends CommunitiesTreeSelectionEvent {
|
||||||
final SpaceModel space;
|
final SpaceModel? space;
|
||||||
final CommunityModel community;
|
final CommunityModel community;
|
||||||
|
|
||||||
const SelectSpaceEvent({required this.space, required this.community});
|
const SelectSpaceEvent({required this.space, required this.community});
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart';
|
||||||
|
|
||||||
|
abstract final class SpaceDetailsDialogHelper {
|
||||||
|
static void showCreate(BuildContext context) {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const SpaceDetailsDialog(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class SpaceDetailsDialog extends StatelessWidget {
|
||||||
|
const SpaceDetailsDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Dialog(
|
||||||
|
child: Text('Create Space'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -102,7 +102,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
))
|
))
|
||||||
.then((v) {
|
.then((v) {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(v);
|
||||||
});
|
});
|
||||||
} else if (state is FailedState) {
|
} else if (state is FailedState) {
|
||||||
visitorBloc.stateDialog(
|
visitorBloc.stateDialog(
|
||||||
@ -476,7 +476,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(null);
|
||||||
},
|
},
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -651,7 +651,7 @@ class VisitorPasswordDialog extends StatelessWidget {
|
|||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop(null);
|
||||||
},
|
},
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
Reference in New Issue
Block a user