Compare commits

..

7 Commits

Author SHA1 Message Date
a56e422bf5 refactor booking page to use PageController and remove legacy implementation 2025-07-23 14:34:59 +03:00
8f71fcb96a enhance week navigation layout for improved UI (#363)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->


## Description

<!--- Describe your changes in detail -->
enhance week navigation layout for improved UI

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-07-23 14:13:27 +03:00
c403048da7 Bugfix/assign tags to devices table overflow (#364)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Description
Fixed overflow in assign tags to devices table.

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-07-23 11:01:56 +03:00
8ae4e561c2 SP-1601-FE-Community-and-Space-Dialog-Redesign-in-the-routine-tab (#346)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1601](https://syncrow.atlassian.net/browse/SP-1601)

## Description

fix reWork notes

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1601]:
https://syncrow.atlassian.net/browse/SP-1601?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-23 10:24:17 +03:00
7dc103f904 Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/assign_togs_to_table_overflow 2025-07-23 09:38:00 +03:00
e4c41bab90 bugfix/assign_tag_to_devices_table_overflow. 2025-07-23 09:37:11 +03:00
59058cf2d2 enhance week navigation layout for improved UI 2025-07-22 14:40:50 +03:00
5 changed files with 185 additions and 228 deletions

View File

@ -1,4 +1,3 @@
// booking_page.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calendar_view/calendar_view.dart';
@ -20,7 +19,8 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class BookingPage extends StatefulWidget {
const BookingPage({super.key});
final PageController? pageController;
const BookingPage({super.key, this.pageController});
@override
State<BookingPage> createState() => _BookingPageState();
@ -42,12 +42,15 @@ class _BookingPageState extends State<BookingPage> {
),
),
],
child: _BookingPageContent(),
child: _BookingPageContent(widget.pageController),
);
}
}
class _BookingPageContent extends StatefulWidget {
final PageController? pageController;
const _BookingPageContent(this.pageController);
@override
State<_BookingPageContent> createState() => _BookingPageContentState();
}
@ -90,8 +93,7 @@ class _BookingPageContentState extends State<_BookingPageContent> {
return BlocListener<SelectedBookableSpaceBloc, SelectedBookableSpaceState>(
listener: (context, state) {
if (state.selectedBookableSpace != null) {
// Reset events and clear cache when room changes
context.read<CalendarEventsBloc>().add(ResetEvents());
context.read<CalendarEventsBloc>().add(const ResetEvents());
_loadEvents(context);
}
},
@ -176,7 +178,9 @@ class _BookingPageContentState extends State<_BookingPageContent> {
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {},
onPressed: () {
widget.pageController!.jumpToPage(2);
},
),
const SizedBox(width: 20),
SvgTextButton(
@ -237,7 +241,6 @@ class _BookingPageContentState extends State<_BookingPageContent> {
.watch<DateSelectionBloc>()
.state
.selectedDateFromSideBarCalender,
// isLoading: eventState is EventsLoading,
);
},
);

View File

@ -19,6 +19,7 @@ class WeekNavigation extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 250,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: ColorsManager.circleRolesBackground,
@ -32,6 +33,8 @@ class WeekNavigation extends StatelessWidget {
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
IconButton(
iconSize: 15,
@ -40,12 +43,16 @@ class WeekNavigation extends StatelessWidget {
onPressed: onPreviousWeek,
),
const SizedBox(width: 10),
Text(
_getMonthYearText(weekStart, weekEnd),
style: const TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 14,
fontWeight: FontWeight.w400,
SizedBox(
width: 120,
child: Text(
_getMonthYearText(weekStart, weekEnd),
style: const TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 14,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
),
const SizedBox(width: 10),

View File

@ -1,57 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class BookingPage extends StatelessWidget {
final PageController pageController;
const BookingPage({
super.key,
required this.pageController,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
Expanded(
child: Container(
color: Colors.blueGrey[100],
child: const Center(
child: Text(
'Side bar',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
)),
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: SizedBox(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {
pageController.jumpToPage(2);
}),
const SizedBox(width: 20),
SvgTextButton(
svgAsset: Assets.groupIcon,
label: 'Manage Users',
onPressed: () {})
],
)
],
),
),
))
],
);
}
}

View File

@ -2,9 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/booking_system/view/booking_page.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/booking_page.dart' hide BookingPage;
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/booking_page.dart';
import 'package:syncrow_web/pages/access_management/view/access_overview_content.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';

View File

@ -31,173 +31,178 @@ class AssignTagsTable extends StatelessWidget {
DataColumn _buildDataColumn(BuildContext context, String label) {
return DataColumn(
label: SelectableText(label, style: context.textTheme.bodyMedium),
label: Expanded(
child: FittedBox(
alignment: AlignmentDirectional.centerStart,
fit: BoxFit.scaleDown,
child: SelectableText(label, style: context.textTheme.bodyMedium),
),
),
);
}
@override
Widget build(BuildContext context) {
return BlocProvider<TagsBloc>(
create: (BuildContext context) => TagsBloc(
create: (context) => TagsBloc(
RemoteTagsService(HTTPService()),
)..add(const LoadTags()),
child: BlocBuilder<TagsBloc, TagsState>(
builder: (context, state) {
return switch (state) {
TagsLoading() || TagsInitial() => const Center(
child: CircularProgressIndicator(),
),
TagsFailure(:final message) => Center(
child: Text(message),
),
TagsLoaded(:final tags) => ClipRRect(
borderRadius: BorderRadius.circular(20),
child: DataTable(
headingRowColor: WidgetStateProperty.all(
ColorsManager.dataHeaderGrey,
),
key: ValueKey(productAllocations.length),
border: TableBorder.all(
color: ColorsManager.dataHeaderGrey,
width: 1,
borderRadius: BorderRadius.circular(20),
),
columns: [
_buildDataColumn(context, '#'),
_buildDataColumn(context, 'Device'),
_buildDataColumn(context, 'Tag'),
_buildDataColumn(context, 'Location'),
],
rows: productAllocations.isEmpty
? [
DataRow(
cells: [
DataCell(
Center(
child: SelectableText(
'No Devices Available',
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.lightGrayColor,
),
),
),
),
DataCell.empty,
DataCell.empty,
DataCell.empty,
],
),
]
: List.generate(productAllocations.length, (index) {
final productAllocation = productAllocations[index];
final allocationUuid = productAllocation.uuid;
final availableTags = tags
.where(
(tag) =>
!productAllocations
.where((p) =>
p.product.productType ==
productAllocation.product.productType)
.map((p) => p.tag.name.toLowerCase())
.contains(tag.name.toLowerCase()) ||
tag.uuid == productAllocation.tag.uuid,
)
.toList();
final currentLocationUuid =
productLocations[allocationUuid];
final currentLocationName = currentLocationUuid == null
? 'Main Space'
: subspaces
.firstWhere((s) => s.uuid == currentLocationUuid)
.name;
return DataRow(
key: ValueKey(allocationUuid),
cells: [
DataCell(Text((index + 1).toString())),
DataCell(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
productAllocation.product.name,
overflow: TextOverflow.ellipsis,
)),
const SizedBox(width: 10),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.lightGrayColor,
width: 1,
),
),
child: IconButton(
icon: const Icon(
Icons.close,
color: ColorsManager.lightGreyColor,
size: 16,
),
onPressed: () {
onProductDeleted(allocationUuid);
},
tooltip: 'Delete Tag',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
),
],
),
),
DataCell(
Container(
alignment: Alignment.centerLeft,
width: double.infinity,
child: ProductTagField(
key: ValueKey('dropdown_$allocationUuid'),
productName: productAllocation.product.uuid,
initialValue: productAllocation.tag,
onSelected: (newTag) {
onTagSelected(allocationUuid, newTag);
},
items: availableTags,
),
),
),
DataCell(
SizedBox(
width: double.infinity,
child: DialogDropdown(
items: [
'Main Space',
...subspaces.map((s) => s.name)
],
selectedValue: currentLocationName,
onSelected: (newLocationName) {
final newSubspaceUuid = newLocationName ==
'Main Space'
? null
: subspaces
.firstWhere(
(s) => s.name == newLocationName)
.uuid;
onLocationSelected(
allocationUuid, newSubspaceUuid);
},
)),
),
],
);
}),
builder: (context, state) => switch (state) {
TagsLoading() || TagsInitial() => const Center(
child: CircularProgressIndicator(),
),
TagsFailure(:final message) => Center(
child: Text(message),
),
TagsLoaded(:final tags) => ClipRRect(
borderRadius: BorderRadius.circular(20),
child: DataTable(
headingRowColor: WidgetStateProperty.all(
ColorsManager.dataHeaderGrey,
),
key: ValueKey(productAllocations.length),
border: TableBorder.all(
color: ColorsManager.dataHeaderGrey,
width: 1,
borderRadius: BorderRadius.circular(20),
),
columns: [
_buildDataColumn(context, '#'),
_buildDataColumn(context, 'Device'),
_buildDataColumn(context, 'Tag'),
_buildDataColumn(context, 'Location'),
],
rows: productAllocations.isEmpty
? [
DataRow(
cells: [
DataCell(
FittedBox(
alignment: AlignmentDirectional.centerStart,
fit: BoxFit.scaleDown,
child: SelectableText(
'No Devices Available',
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.lightGrayColor,
),
),
),
),
DataCell.empty,
DataCell.empty,
DataCell.empty,
],
),
]
: List.generate(productAllocations.length, (index) {
final productAllocation = productAllocations[index];
final allocationUuid = productAllocation.uuid;
final availableTags = tags
.where(
(tag) =>
!productAllocations
.where((p) =>
p.product.productType ==
productAllocation.product.productType)
.map((p) => p.tag.name.toLowerCase())
.contains(tag.name.toLowerCase()) ||
tag.uuid == productAllocation.tag.uuid,
)
.toList();
final currentLocationUuid = productLocations[allocationUuid];
final currentLocationName = currentLocationUuid == null
? 'Main Space'
: subspaces
.firstWhere((s) => s.uuid == currentLocationUuid)
.name;
return DataRow(
key: ValueKey(allocationUuid),
cells: [
DataCell(Text((index + 1).toString())),
DataCell(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
productAllocation.product.name,
overflow: TextOverflow.ellipsis,
)),
const SizedBox(width: 10),
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.lightGrayColor,
width: 1,
),
),
child: IconButton(
icon: const Icon(
Icons.close,
color: ColorsManager.lightGreyColor,
size: 16,
),
onPressed: () {
onProductDeleted(allocationUuid);
},
tooltip: 'Delete Tag',
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
),
],
),
),
DataCell(
Container(
alignment: Alignment.centerLeft,
width: double.infinity,
child: ProductTagField(
key: ValueKey('dropdown_$allocationUuid'),
productName: productAllocation.product.uuid,
initialValue: productAllocation.tag,
onSelected: (newTag) {
onTagSelected(allocationUuid, newTag);
},
items: availableTags,
),
),
),
DataCell(
SizedBox(
width: double.infinity,
child: DialogDropdown(
items: [
'Main Space',
...subspaces.map((s) => s.name)
],
selectedValue: currentLocationName,
onSelected: (newLocationName) {
final newSubspaceUuid = newLocationName ==
'Main Space'
? null
: subspaces
.firstWhere(
(s) => s.name == newLocationName)
.uuid;
onLocationSelected(
allocationUuid, newSubspaceUuid);
},
)),
),
],
);
}),
),
_ => const SizedBox.shrink(),
};
),
_ => const SizedBox.shrink(),
},
),
);