Compare commits

...

96 Commits

Author SHA1 Message Date
9a4fdb2f88 Sp 1722 duplicate space dialog enhancement (#370)
<!--
  Thanks for contributing!

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

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

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

## Description

Implemented naming validation, that disallows for any name duplication.
Adds proper name suffix to the duplicated space's name, if there is any
match with the siblings.
Enhanced the dialog's design to match the design language of the
application.

## Type of Change

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

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


[SP-1722]:
https://syncrow.atlassian.net/browse/SP-1722?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-27 11:20:39 +03:00
1558806cc3 Refactor SpaceDetailsForm to use StatefulWidget for form validation and improve user experience with dynamic save button state, and added space name validation to not be longer than 50 characters. 2025-07-27 11:18:08 +03:00
a4391aa73e Made AssignTagsDialog scrollable to account for a long list of product allocations, for a better UX. 2025-07-27 11:05:30 +03:00
2d69e3c72f Made SpaceSubSpacesDialog scrollable, for a better UX, and to account for a very long list of subspaces. 2025-07-27 11:01:22 +03:00
cd8ffc99ea removed unnecessary flag, that can be replaced with checking if the value equals null. 2025-07-27 10:56:54 +03:00
83895d3dda Diallows naming duplication when duplicating a space, and adds a proper suffix to the name in case of another spaces having a name match. 2025-07-27 10:51:53 +03:00
f1cf8d88d3 Matched design of duplicate space dialog, with the design language of the system. 2025-07-24 16:40:03 +03:00
0f9cbd22a2 SP-1972-delete-space-reworks (#368)
<!--
  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-1972](https://syncrow.atlassian.net/browse/SP-1972)

## Description

Enhanced UI design of delete space feature.

## 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-1972]:
https://syncrow.atlassian.net/browse/SP-1972?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-24 16:24:19 +03:00
9d0a1f4883 Refactor SpaceManagementSidebarHeader to integrate CustomSearchBar and improve layout 2025-07-24 15:04:10 +03:00
7d77809750 Refactor CustomExpansionTile and SpaceDetailsDialogHelper for improved functionality and UI consistency
- Updated CustomExpansionTile to enhance selection state handling and UI responsiveness.
- Integrated success and error snackbars in SpaceDetailsDialogHelper for better user feedback during space creation and updates.
- Removed redundant comments and improved code readability.
2025-07-24 14:51:07 +03:00
56a03443be Fix booking API endpoint month format by changing separator from '/'to '-'. (#369)
<!--
  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 -->

## 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-24 13:29:38 +03:00
5a35f8e62e Fix booking API endpoint month format by changing separator from '/' to '-'. 2025-07-24 13:19:55 +03:00
6943ee07ac - Updated the onSave callback in SpaceSubSpacesDialog to accept product allocations, allowing for better management of subspace data. 2025-07-24 12:38:24 +03:00
47340c3235 Rework fixes. 2025-07-24 12:34:06 +03:00
527c04de9a Refactor SpaceDetailsActionButtons to improve button layout and spacing 2025-07-24 12:06:58 +03:00
1448aa97ce Refactor SpaceDetailsDialog to improve title handling and loading/error dialogs 2025-07-24 12:05:24 +03:00
16b79ae12f Enhanced sizing of SpaceDetailsForm and SpaceIconPicker. 2025-07-24 12:02:54 +03:00
6dbb6b6a6e Made Create Space dialog title match the figma design. 2025-07-24 11:59:46 +03:00
c4ed30f539 Enhance ButtonContentWidget to support customizable icon dimensions. 2025-07-24 11:59:21 +03:00
99924c1e62 Refactor color management and UI components for consistency
- Updated color references in various widgets to use the new `opaquePrimary` color for better visual consistency.
- Refactored `ColorsManager` to improve color definitions and removed redundant color declarations.
- Enhanced UI elements across multiple dialogs and widgets to ensure a cohesive design language.

This change promotes maintainability and aligns with the updated color scheme.
2025-07-24 10:27:17 +03:00
04d1c37308 SP-1917-FE-Increase-Zoom-In-Out-levels-of-the-canvas. (#367)
<!--
  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-1917](https://syncrow.atlassian.net/browse/SP-1917)

## Description

Increased zoom in/out levels of the space management canvas.

## Type of Change

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

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


[SP-1917]:
https://syncrow.atlassian.net/browse/SP-1917?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-24 10:21:50 +03:00
dfdf4fb27c Sp 1722 fe implement duplicate space feature (#365)
<!--
  Thanks for contributing!

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

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

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

## Description

Implemented a feature that allows users to duplicate a space.

## Type of Change

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

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


[SP-1722]:
https://syncrow.atlassian.net/browse/SP-1722?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-24 10:21:35 +03:00
3859dc67d8 SP-1917-FE-Increase-Zoom-In-Out-levels-of-the-canvas. 2025-07-24 09:57:23 +03:00
ae3eb6fca8 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1722-FE-Implement-Duplicate-Space-Feature 2025-07-24 09:52:06 +03:00
f341dcd482 Remove unnecessary event dispatch in CommunityStructureHeaderActionButtonsComposer to streamline community update logic. This change enhances code clarity by eliminating the selection event for the community, focusing solely on the update action. 2025-07-24 09:47:39 +03:00
e98b091253 Refactor SpaceManagementBody to use a Stack layout for improved UI structure, allowing better positioning of the SpaceManagementCommunitiesTree and the main content. Enhance SpaceManagementCommunitiesTree with a shadow effect for better visual separation. This change promotes a more organized and visually appealing interface. 2025-07-24 09:39:38 +03:00
77d6d822cb Refactor SpaceSubSpacesDialog and SubSpacesInput to integrate a shared TextEditingController for improved state management of subspace names. This change enhances the input handling and ensures proper disposal of the controller, promoting better resource management. 2025-07-24 09:39:33 +03:00
f1aab13263 refactor booking page to use PageController (#366)
<!--
  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 -->
refactor booking page to use PageController 

## 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:37:43 +03:00
a56e422bf5 refactor booking page to use PageController and remove legacy implementation 2025-07-23 14:34:59 +03:00
97530dd351 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1722-FE-Implement-Duplicate-Space-Feature 2025-07-23 14:19:55 +03:00
a57b6e0853 Enhance CommunityStructureCanvas by adding a _centerOnTree method to improve the centering logic of the community structure. This method calculates the optimal view based on the positions of spaces and adjusts the transformation controller accordingly, ensuring a smoother user experience during updates and animations. 2025-07-23 14:19:25 +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
845397e819 Update CommunityStructureCanvas to improve widget update logic by animating to the selected space based on community UUID changes. This enhances the responsiveness of the UI when the community context changes. 2025-07-23 13:21:49 +03:00
2077ef053f Refactor DuplicateSpaceService to return a list of SpaceModel objects instead of a single instance. Update related components including DuplicateSpaceSuccess state and DuplicateSpaceDialog to handle multiple spaces. Enhance CommunityStructureHeaderActionButtonsComposer to reflect these changes in the success callback. 2025-07-23 12:42:23 +03:00
d21850edc8 Enhance DuplicateSpaceDialog to use Bloc for state management and streamline the duplication process with success and error handling. Update CommunityStructureHeaderActionButtonsComposer to integrate the new dialog for duplicating spaces. 2025-07-23 12:37:27 +03:00
85f53ed1f2 Remove DuplicateSpaceBloc and its associated service from SpaceManagementPage to streamline dependencies and improve code clarity. 2025-07-23 12:37:17 +03:00
5fde74fc7d Add DuplicateSpaceFailureDialog widget to display error messages when duplicating spaces fails. 2025-07-23 12:37:07 +03:00
994efc302b Add AppSnackBarsBuildContextExtension for displaying success and failure snack bars in the app. 2025-07-23 12:36:45 +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
04b7a506be Remove newSpaceIcon parameter from DuplicateSpaceParam class since it isnt needed. 2025-07-23 10:52:46 +03:00
19ddf443a9 Refactor RemoteDuplicateSpaceService to improve code readability by aligning method chaining for URL replacements. 2025-07-23 10:52:29 +03:00
3779176978 Add DuplicateSpaceDialog widget for user interaction in duplicate space management. 2025-07-23 10:52:18 +03:00
7c5bca35fc Add DuplicateSpaceTextField widget for user input in duplicate space management. 2025-07-23 10:52:07 +03:00
aed3004a31 Added DuplicateSpaceBloc to SpaceManagementPage for managing duplicate space functionality. 2025-07-23 10:51:51 +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
4241d11cb6 Implemented duplicate_space data layer. 2025-07-23 09:59:13 +03:00
ef8c9efff0 Added duplicate space endpoint to ApiEndpoints. 2025-07-23 09:58:46 +03:00
c59d2b7fd6 Implemented toJson method in DuplicateSpaceParam. 2025-07-23 09:58:15 +03:00
71f0da9299 Created duplicate_space presentation layer. 2025-07-23 09:51:09 +03:00
e6d9000ee2 Implemented duplicate space domain layer. 2025-07-23 09:48:23 +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
7f3dfebf15 Merge branch 'dev' into SP-1601-FE-Community-and-Space-Dialog-Redesign-in-the-routine-tab 2025-07-23 09:34:54 +03:00
0de882d43b [FE] On Uba Gateway device the Icons of the devices inside are not all of them displayed (#359)
<!--
  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-1569](https://syncrow.atlassian.net/browse/SP-1569)

## Description

add devices and icons for them

## 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-1569]:
https://syncrow.atlassian.net/browse/SP-1569?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-23 09:34:10 +03:00
6a737e5d43 [FE] Manage Bookable Spaces Tab (#355)
<!--
  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-1693](https://syncrow.atlassian.net/browse/SP-1693)
[SP-1694](https://syncrow.atlassian.net/browse/SP-1694)

## Description

all about unbookable spaces is Ready

## Type of Change

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

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


[SP-1693]:
https://syncrow.atlassian.net/browse/SP-1693?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[SP-1694]:
https://syncrow.atlassian.net/browse/SP-1694?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-23 09:33:46 +03:00
c323d88790 Feature/reorder spaces api integration (#362)
<!--
  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

Integrated reordering spaces with the API.
Fixed drop target bug, where the canvas wouldn't show the first drop
target in the tree.

## Type of Change

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

- [ x ]  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 09:31:16 +03:00
68153e41ed remove print statement 2025-07-23 09:02:30 +03:00
6a7174b929 no need for selectedbookableSpaces list anymore instead i am using state list 2025-07-22 16:47:50 +03:00
59058cf2d2 enhance week navigation layout for improved UI 2025-07-22 14:40:50 +03:00
b6d4084ca7 fix settings and back button to meet the UI 2025-07-22 10:58:30 +03:00
2b110b7c91 fix pagination UI 2025-07-22 10:50:00 +03:00
066fe4bc95 make variable final 2025-07-22 10:35:06 +03:00
85d65b2d96 Merge branch 'Implement-Spaces-Table-Empty-Filled-Failure-states-bookable-spaces' of https://github.com/SyncrowIOT/web into Implement-Spaces-Table-Empty-Filled-Failure-states-bookable-spaces 2025-07-22 10:12:14 +03:00
a64a9f1d12 fix unactive color 2025-07-22 10:12:02 +03:00
94f9c1beea Adjust layout in CommunityStructureCanvas by adding horizontal padding to positions and refining target position calculations for improved spacing and alignment. Enhance Stack widget behavior by allowing overflow clipping. 2025-07-22 10:04:38 +03:00
a7487f5434 Merge branch 'dev' into Implement-Spaces-Table-Empty-Filled-Failure-states-bookable-spaces 2025-07-22 09:58:14 +03:00
c0b74162e9 design fixes 2025-07-22 09:56:32 +03:00
dfd8c5fa31 Replace Container with AnimatedContainer in CommunityStructureCanvas to enhance visual feedback during state changes. Adjust alpha value for improved visibility based on candidate data presence. 2025-07-22 09:46:56 +03:00
60b8ee8b50 Enhance DragTarget logic in CommunityStructureCanvas by refining conditions for rendering and improving readability. Ensure proper handling of dragged data and its parent/community relationships. 2025-07-22 09:37:31 +03:00
9d60f913eb Refactor CommunityStructureCanvas to simplify DragTarget logic by replacing SizedBox with SizedBox.shrink() for better performance and readability. 2025-07-22 09:32:57 +03:00
40251b846b Integrate ReorderSpaces functionality into CommunityStructureCanvas and enhance RemoteReorderSpacesService with dynamic URL generation. Update ReorderSpacesParam to require parentSpaceUuid and spaces for improved validation and serialization. 2025-07-21 16:43:26 +03:00
1323bceca1 Update ReorderSpacesParam to make parentSpaceUuid optional and add toJson method for serialization. 2025-07-21 16:39:31 +03:00
0eb4652f26 Enhance garage door scheduling functionality and UI improvements (#358)
<!--
  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 garage door scheduling functionality and UI improvements

## 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-21 16:25:53 +03:00
460639b681 Refactor booking system: update API endpoint format, add ResetEvents event, and enhance UI components for improved user experience (#361)
<!--
  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 -->
Refactor booking system: update API endpoint format, add ResetEvents
event, and enhance UI components for improved user experience

## 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-21 16:25:34 +03:00
35c8a73156 Refactor SpaceManagementPage's initState to ensure HTTPService is initialized before use in CommunitiesBloc. 2025-07-21 16:24:11 +03:00
ce65b068ff Merge branch 'dev' of https://github.com/SyncrowIOT/web into feature/reorder_spaces_api_integration 2025-07-21 16:22:22 +03:00
96f107f972 Refactor SpaceManagementPage to utilize a shared HTTPService instance for API calls in Communities, SpaceDetails, Products, and ReorderSpaces blocs, and injected ReorderSpacesBloc into it. 2025-07-21 16:15:26 +03:00
a3a7937021 Implemented ReorderSpacesBloc. 2025-07-21 16:14:03 +03:00
9bf715501b Implement ReorderSpacesService. 2025-07-21 15:57:31 +03:00
c65f4a7fab Add ReorderSpacesParam and ReorderSpacesService for managing space reordering functionality. 2025-07-21 15:57:20 +03:00
7af8887d4f Add new API endpoint for reordering spaces in the community module. 2025-07-21 15:57:10 +03:00
2f89c3486c Release incomplete revamped space management (#360)
<!--
  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 User Story
[SP-1686](https://syncrow.atlassian.net/browse/SP-1686)

## Description
Released the new space management rewritten version, which for now
includes, CRUD operations for spaces, and community tree with the new
API.

Rewrote main files, since they had a lot of code duplication, i
extracted duplicated code into one component for all flavors to use.

Enhanced routing solution, to make sure there's only one instance of the
router, as the documentation of `go_router` guides us to do.

## Type of Change

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

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


[SP-1686]:
https://syncrow.atlassian.net/browse/SP-1686?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-21 15:33:06 +03:00
5589e5b587 Refactor booking system: update API endpoint format, add ResetEvents event, and enhance UI components for improved user experience 2025-07-21 15:22:17 +03:00
d3bd363b70 Fix category check in schedule dialog for water heater 2025-07-21 15:10:15 +03:00
3a4fce966c Merge branch 'dev' of https://github.com/SyncrowIOT/web into release_incomplete_revamped_space_management 2025-07-21 15:06:40 +03:00
6bdd28ec57 Refactor main entry points to utilize SyncrowApp, removing legacy MyApp implementation and associated dependencies, since there was too much duplicated code. 2025-07-21 14:50:29 +03:00
652163fdae Updated Space management route in app_routes.dart to use the new, incomplete, revamped space management. 2025-07-21 14:47:42 +03:00
b738596b50 no need for colors instead use ColorsManager 2025-07-21 09:14:59 +03:00
0ad562b6ce add icons and types for devices did not added before 2025-07-20 14:03:50 +03:00
995ce480cb requested note 2025-07-17 17:44:30 +03:00
076c80fe44 Enhance garage door scheduling functionality and UI improvements 2025-07-17 17:02:23 +03:00
b41733ee40 Merge branch 'SP-1601-FE-Community-and-Space-Dialog-Redesign-in-the-routine-tab' of https://github.com/SyncrowIOT/web into SP-1601-FE-Community-and-Space-Dialog-Redesign-in-the-routine-tab 2025-07-15 11:53:29 +03:00
fe090175e3 fix Typo 2025-07-15 11:52:57 +03:00
85544c69f8 Merge branch 'dev' into SP-1601-FE-Community-and-Space-Dialog-Redesign-in-the-routine-tab 2025-07-15 09:09:18 +03:00
9f71bbff63 fix color uses 2025-07-15 08:26:40 +03:00
deb227034a fix Rework notes 2025-07-11 11:44:16 +03:00
247 changed files with 3346 additions and 2296 deletions

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.89852 11.08C-0.632759 8.54864 -0.632708 4.42983 1.89852 1.8985C4.42985 -0.632833 8.54861 -0.632833 11.0799 1.8985C13.2274 4.04599 13.5526 7.23986 12.0565 9.73392C12.0565 9.73392 11.949 9.91422 12.0942 10.0593C12.9222 10.8872 15.4065 13.3716 15.4065 13.3716C16.0658 14.0308 16.2227 14.9527 15.638 15.5374L15.5374 15.638C14.9527 16.2227 14.0308 16.0658 13.3716 15.4065C13.3716 15.4065 10.8926 12.9275 10.0662 12.1012C9.91414 11.9491 9.73389 12.0566 9.73389 12.0566C7.23988 13.5526 4.04601 13.2275 1.89852 11.08ZM9.88131 9.88133C11.7517 8.01094 11.7516 4.96763 9.88125 3.09724C8.01086 1.22689 4.96755 1.22684 3.09721 3.09724C1.22681 4.96758 1.22681 8.01094 3.09721 9.88133C4.9676 11.7516 8.01086 11.7516 9.88131 9.88133Z" fill="#999999" fill-opacity="0.7"/>
<path d="M9.46701 6.10386C9.55406 6.10386 9.64256 6.08674 9.72786 6.05072C10.0686 5.9065 10.228 5.51333 10.0838 5.17253C9.17738 3.03045 6.69729 2.0252 4.55526 2.93164C4.21451 3.07586 4.05509 3.46903 4.1993 3.80983C4.34357 4.15063 4.73664 4.30995 5.07755 4.16579C6.539 3.54737 8.23126 4.23326 8.84962 5.69471C8.95781 5.95031 9.20594 6.10386 9.46701 6.10386Z" fill="#999999" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,9 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555643 0.555643 0 1.24111 0H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79675 39.5036 2.48221L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
<path d="M38.7585 0H35.0352C35.7206 0 36.2763 0.555643 36.2763 1.24111V38.7589C36.2763 39.4444 35.7206 40 35.0352 40H38.7585C39.4439 40 39.9996 39.4444 39.9996 38.7589V1.24111C39.9996 0.555643 39.4439 0 38.7585 0Z" fill="#D1D1D1"/>
<path opacity="0.6" d="M12.0283 31.8319V33.3212C12.0283 34.0067 11.6086 34.5623 11.0908 34.5623H6.96582C6.44804 34.5623 6.02832 34.0067 6.02832 33.3212V31.8319C6.02832 31.1465 6.44804 30.5908 6.96582 30.5908H11.0908C11.6086 30.5908 12.0283 31.1465 12.0283 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M12.0283 7.24109V8.73042C12.0283 9.41588 11.6086 9.97153 11.0908 9.97153H6.96582C6.44804 9.97153 6.02832 9.41588 6.02832 8.73042V7.24109C6.02832 6.55563 6.44804 5.99998 6.96582 5.99998H11.0908C11.6086 5.99998 12.0283 6.55563 12.0283 7.24109Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M26.0283 31.8319V33.3212C26.0283 34.0067 26.448 34.5623 26.9658 34.5623H31.0908C31.6086 34.5623 32.0283 34.0067 32.0283 33.3212V31.8319C32.0283 31.1465 31.6086 30.5908 31.0908 30.5908H26.9658C26.448 30.5908 26.0283 31.1465 26.0283 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M26.0283 7.24109V8.73042C26.0283 9.41588 26.448 9.97153 26.9658 9.97153H31.0908C31.6086 9.97153 32.0283 9.41588 32.0283 8.73042V7.24109C32.0283 6.55563 31.6086 5.99998 31.0908 5.99998H26.9658C26.448 5.99998 26.0283 6.55563 26.0283 7.24109Z" fill="#023DFE" fill-opacity="0.5"/>
<path d="M19.0693 0H20.931V40H19.0693V0Z" fill="#D1D1D1"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,12 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555651 0.555643 7.62939e-06 1.24111 7.62939e-06H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79676 39.5036 2.48222L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
<path d="M38.7585 0H35.0352C35.7206 0 36.2763 0.555643 36.2763 1.24111V38.7589C36.2763 39.4444 35.7206 40 35.0352 40H38.7585C39.4439 40 39.9996 39.4444 39.9996 38.7589V1.24111C39.9996 0.555643 39.4439 0 38.7585 0Z" fill="#D1D1D1"/>
<path opacity="0.6" d="M8.64062 31.8319V33.3212C8.64062 34.0067 8.22091 34.5623 7.70312 34.5623H3.57813C3.06034 34.5623 2.64062 34.0067 2.64062 33.3212V31.8319C2.64062 31.1464 3.06034 30.5908 3.57813 30.5908H7.70312C8.22091 30.5908 8.64062 31.1464 8.64062 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M8.64062 7.24109V8.73042C8.64062 9.41588 8.22091 9.97152 7.70312 9.97152H3.57813C3.06034 9.97152 2.64062 9.41588 2.64062 8.73042V7.24109C2.64062 6.55563 3.06034 5.99998 3.57813 5.99998H7.70312C8.22091 5.99998 8.64062 6.55563 8.64062 7.24109Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M27.6406 31.8319V33.3212C27.6406 34.0067 28.0603 34.5623 28.5781 34.5623H32.7031C33.2209 34.5623 33.6406 34.0067 33.6406 33.3212V31.8319C33.6406 31.1464 33.2209 30.5908 32.7031 30.5908H28.5781C28.0603 30.5908 27.6406 31.1464 27.6406 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M27.6406 7.24109V8.73042C27.6406 9.41588 28.0603 9.97152 28.5781 9.97152H32.7031C33.2209 9.97152 33.6406 9.41588 33.6406 8.73042V7.24109C33.6406 6.55563 33.2209 5.99998 32.7031 5.99998H28.5781C28.0603 5.99998 27.6406 6.55563 27.6406 7.24109Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M15.0625 31.8319V33.3212C15.0625 34.0067 15.4822 34.5623 16 34.5623H20.125C20.6428 34.5623 21.0625 34.0067 21.0625 33.3212V31.8319C21.0625 31.1464 20.6428 30.5908 20.125 30.5908H16C15.4822 30.5908 15.0625 31.1464 15.0625 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
<path opacity="0.6" d="M15.0625 7.24109V8.73042C15.0625 9.41588 15.4822 9.97152 16 9.97152H20.125C20.6428 9.97152 21.0625 9.41588 21.0625 8.73042V7.24109C21.0625 6.55563 20.6428 5.99998 20.125 5.99998H16C15.4822 5.99998 15.0625 6.55563 15.0625 7.24109Z" fill="#023DFE" fill-opacity="0.5"/>
<path d="M23.125 0H24.9867V40H23.125V0Z" fill="#D1D1D1"/>
<path d="M11.1719 0H13.0335V40H11.1719V0Z" fill="#D1D1D1"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -66,7 +66,7 @@ class _DialogDropdownState extends State<DialogDropdown> {
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
constraints: const BoxConstraints(
maxHeight: 200.0, // Set max height for dropdown
),
@ -87,12 +87,10 @@ class _DialogDropdownState extends State<DialogDropdown> {
child: ListTile(
title: Text(
item,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.textPrimaryColor,
),
style:
Theme.of(context).textTheme.bodyMedium?.copyWith(
color: ColorsManager.textPrimaryColor,
),
),
onTap: () {
widget.onSelected(item);

View File

@ -14,7 +14,7 @@ class EditChip extends StatelessWidget {
this.label = 'Edit',
required this.onTap,
this.labelColor = ColorsManager.spaceColor,
this.backgroundColor = ColorsManager.whiteColors,
this.backgroundColor = ColorsManager.white,
this.borderColor = ColorsManager.spaceColor,
this.borderRadius = 16.0,
}) : super(key: key);
@ -24,10 +24,9 @@ class EditChip extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Chip(
label: Text(
label,
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)
),
label: Text(label,
style:
Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)),
backgroundColor: backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),

View File

@ -17,8 +17,7 @@ class TagDialogTextfieldDropdown extends StatefulWidget {
}) : super(key: key);
@override
_DialogTextfieldDropdownState createState() =>
_DialogTextfieldDropdownState();
_DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState();
}
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
@ -97,7 +96,7 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
constraints: const BoxConstraints(maxHeight: 200.0),
child: StatefulBuilder(
builder: (context, setStateDropdown) {
@ -122,8 +121,7 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager
.textPrimaryColor)),
color: ColorsManager.textPrimaryColor)),
onTap: () {
_controller.text = tag.tag ?? '';
widget.onSelected(tag);

View File

@ -5,19 +5,20 @@ class CustomExpansionTile extends StatefulWidget {
final String title;
final List<Widget>? children;
final bool initiallyExpanded;
final bool isSelected; // Add this to track selection
final bool? isExpanded; // External control over expansion
final ValueChanged<bool>? onExpansionChanged; // Notify when expansion changes
final VoidCallback? onItemSelected; // Callback for selecting the item
final bool isSelected;
final bool? isExpanded;
final ValueChanged<bool>? onExpansionChanged;
final VoidCallback? onItemSelected;
CustomExpansionTile({
const CustomExpansionTile({
super.key,
required this.title,
this.children,
this.initiallyExpanded = false,
this.isExpanded, // Allow external control over expansion
this.onExpansionChanged, // Notify when expansion changes
this.onItemSelected, // Trigger item selection when name is tapped
required this.isSelected, // Add this to initialize selection state
this.isExpanded,
this.onExpansionChanged,
this.onItemSelected,
required this.isSelected,
});
@override
@ -25,7 +26,7 @@ class CustomExpansionTile extends StatefulWidget {
}
class CustomExpansionTileState extends State<CustomExpansionTile> {
bool _isExpanded = false; // Local expansion state
bool _isExpanded = false;
@override
void initState() {
@ -36,7 +37,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
@override
void didUpdateWidget(CustomExpansionTile oldWidget) {
super.didUpdateWidget(oldWidget);
// Sync local state with external control of expansion state
if (widget.isExpanded != null && widget.isExpanded != _isExpanded) {
setState(() {
_isExpanded = widget.isExpanded!;
@ -44,7 +44,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
}
}
// Utility function to capitalize the first letter of the title
String _capitalizeFirstLetter(String text) {
if (text.isEmpty) return text;
return text[0].toUpperCase() + text.substring(1);
@ -56,7 +55,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
children: [
Row(
children: [
// Expand/collapse icon, now wrapped in a GestureDetector for specific onTap
if (widget.children != null && widget.children!.isNotEmpty)
GestureDetector(
onTap: () {
@ -70,38 +68,33 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_right,
color: ColorsManager.lightGrayColor,
size: 16.0, // Adjusted size for better alignment
size: 16,
),
),
// The title text, wrapped in GestureDetector to handle selection
Expanded(
child: GestureDetector(
onTap: () {
if (widget.onItemSelected != null) {
widget.onItemSelected!();
widget.onItemSelected!.call();
}
},
child: Text(
_capitalizeFirstLetter(widget.title),
style: TextStyle(
color: widget.isSelected
? ColorsManager
.blackColor // Change color to black when selected
: ColorsManager
.lightGrayColor, // Gray when not selected
fontWeight: FontWeight.w400,
? ColorsManager.blackColor
: ColorsManager.lightGrayColor,
fontWeight:
widget.isSelected ? FontWeight.w600 : FontWeight.w400,
),
),
),
),
],
),
// The expanded section (children) that shows when the tile is expanded
if (_isExpanded &&
widget.children != null &&
widget.children!.isNotEmpty)
if (_isExpanded && widget.children != null && widget.children!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 24.0), // Indented children
padding: const EdgeInsets.only(left: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.children!,

View File

@ -7,7 +7,7 @@ class CustomSearchBar extends StatefulWidget {
final TextEditingController? controller;
final String hintText;
final String? searchQuery;
final Function(String)? onSearchChanged; // Callback for search input changes
final void Function(String)? onSearchChanged;
const CustomSearchBar({
super.key,
@ -34,10 +34,10 @@ class _CustomSearchBarState extends State<CustomSearchBar> {
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
color: ColorsManager.shadowBlackColor.withValues(alpha: 0.1),
spreadRadius: 0,
blurRadius: 8,
offset: const Offset(0, 4),
@ -57,7 +57,7 @@ class _CustomSearchBarState extends State<CustomSearchBar> {
style: const TextStyle(
color: Colors.black,
),
onChanged: widget.onSearchChanged, // Call the callback on text change
onChanged: widget.onSearchChanged,
decoration: InputDecoration(
filled: true,
fillColor: ColorsManager.textFieldGreyColor,

View File

@ -1,22 +1,9 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
import 'package:syncrow_web/syncrow_app.dart';
Future<void> main() async {
try {
@ -33,59 +20,5 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
runApp(const SyncrowApp());
}

View File

@ -1,22 +1,9 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
import 'package:syncrow_web/syncrow_app.dart';
Future<void> main() async {
try {
@ -33,59 +20,5 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
runApp(const SyncrowApp());
}

View File

@ -1,26 +1,16 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart';
import 'package:syncrow_web/syncrow_app.dart';
Future<void> main() async {
try {
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
const environment = String.fromEnvironment(
'FLAVOR',
defaultValue: 'staging',
);
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
@ -30,59 +20,5 @@ Future<void> main() async {
);
initialSetup();
} catch (_) {}
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
routes: AppRoutes.getRoutes(),
redirect: (context, state) async {
final checkToken = await AuthBloc.getTokenAndValidate();
final loggedIn = checkToken == 'Success';
final goingToLogin = state.uri.toString() == RoutesConst.auth;
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
if (loggedIn && goingToLogin) return RoutesConst.home;
return null;
},
);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(),
),
],
child: MaterialApp.router(
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));
}
runApp(const SyncrowApp());
}

View File

@ -10,7 +10,7 @@ part 'events_event.dart';
part 'events_state.dart';
class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
final EventController eventController = EventController();
EventController eventController = EventController();
final CalendarSystemService calendarService;
CalendarEventsBloc({
@ -20,7 +20,9 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
on<AddEvent>(_onAddEvent);
on<DisposeResources>(_onDisposeResources);
on<GoToWeek>(_onGoToWeek);
on<ResetEvents>(_onResetEvents);
}
Future<void> _onLoadEvents(
LoadEvents event,
Emitter<CalendarEventState> emit,
@ -126,4 +128,18 @@ class CalendarEventsBloc extends Bloc<CalendarEventsEvent, CalendarEventState> {
eventController.dispose();
return super.close();
}
void _onResetEvents(
ResetEvents event,
Emitter<CalendarEventState> emit,
) {
if (calendarService is MemoryCalendarServiceWithRemoteFallback) {
(calendarService as MemoryCalendarServiceWithRemoteFallback)
.memoryService
.clear();
}
eventController.dispose();
eventController = EventController();
emit(EventsInitial());
}
}

View File

@ -29,3 +29,10 @@ class CheckWeekHasEvents extends CalendarEventsEvent {
final DateTime weekStart;
const CheckWeekHasEvents(this.weekStart);
}
class ResetEvents extends CalendarEventsEvent {
const ResetEvents();
@override
List<Object?> get props => [];
}

View File

@ -1,6 +1,7 @@
import 'package:calendar_view/calendar_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:calendar_view/calendar_view.dart';
import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/data/services/remote_calendar_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/domain/LoadEventsParam.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/calendar/events_bloc.dart';
@ -8,7 +9,6 @@ import 'package:syncrow_web/pages/access_management/booking_system/presentation/
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_event.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/date_selection/date_selection_state.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/bloc/selected_bookable_space_bloc/selected_bookable_space_bloc.dart';
import 'package:syncrow_web/pages/access_management/booking_system/data/services/memory_bookable_space_service.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/booking_sidebar.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/custom_calendar_page.dart';
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
@ -19,14 +19,44 @@ 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();
}
class _BookingPageState extends State<BookingPage> {
late final EventController _eventController;
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => SelectedBookableSpaceBloc()),
BlocProvider(create: (_) => DateSelectionBloc()),
BlocProvider(
create: (_) => CalendarEventsBloc(
calendarService: MemoryCalendarServiceWithRemoteFallback(
remoteService: RemoteCalendarService(HTTPService()),
memoryService: MemoryCalendarService(),
),
),
),
],
child: _BookingPageContent(widget.pageController),
);
}
}
class _BookingPageContent extends StatefulWidget {
final PageController? pageController;
const _BookingPageContent(this.pageController);
@override
State<_BookingPageContent> createState() => _BookingPageContentState();
}
class _BookingPageContentState extends State<_BookingPageContent> {
late EventController _eventController;
@override
void initState() {
@ -40,7 +70,7 @@ class _BookingPageState extends State<BookingPage> {
super.dispose();
}
void _dispatchLoadEvents(BuildContext context) {
void _loadEvents(BuildContext context) {
final selectedRoom =
context.read<SelectedBookableSpaceBloc>().state.selectedBookableSpace;
final dateState = context.read<DateSelectionBloc>().state;
@ -60,186 +90,164 @@ class _BookingPageState extends State<BookingPage> {
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (_) => SelectedBookableSpaceBloc()),
BlocProvider(create: (_) => DateSelectionBloc()),
BlocProvider(
create: (_) => CalendarEventsBloc(
calendarService: MemoryCalendarServiceWithRemoteFallback(
remoteService: RemoteCalendarService(
HTTPService(),
),
memoryService: MemoryCalendarService(),
),
)),
],
child: Builder(
builder: (context) =>
BlocListener<CalendarEventsBloc, CalendarEventState>(
listenWhen: (prev, curr) => curr is EventsLoaded,
return BlocListener<SelectedBookableSpaceBloc, SelectedBookableSpaceState>(
listener: (context, state) {
if (state.selectedBookableSpace != null) {
context.read<CalendarEventsBloc>().add(const ResetEvents());
_loadEvents(context);
}
},
child: BlocListener<DateSelectionBloc, DateSelectionState>(
listener: (context, state) {
_loadEvents(context);
},
child: BlocListener<CalendarEventsBloc, CalendarEventState>(
listener: (context, state) {
if (state is EventsLoaded) {
_eventController.removeWhere((_) => true);
_eventController.addAll(state.events);
}
},
child: BlocListener<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
listener: (context, state) => _dispatchLoadEvents(context),
child: BlocListener<DateSelectionBloc, DateSelectionState>(
listener: (context, state) => _dispatchLoadEvents(context),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(3, 0),
blurRadius: 6,
spreadRadius: 0,
),
],
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.white,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(3, 0),
blurRadius: 6,
spreadRadius: 0,
),
child: Column(
children: [
Expanded(
flex: 2,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, state) {
return BookingSidebar(
onRoomSelected: (selectedRoom) {
context
.read<SelectedBookableSpaceBloc>()
.add(SelectBookableSpace(selectedRoom));
},
);
},
),
),
Expanded(
child: BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return CustomCalendarPage(
selectedDate: dateState.selectedDate,
onDateChanged: (day, month, year) {
final newDate = DateTime(year, month, day);
context
.read<DateSelectionBloc>()
.add(SelectDate(newDate));
context.read<DateSelectionBloc>().add(
SelectDateFromSidebarCalendar(newDate));
},
);
},
),
),
],
),
),
],
),
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Column(
children: [
Expanded(
flex: 2,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, state) {
return BookingSidebar(
onRoomSelected: (selectedRoom) {
context
.read<SelectedBookableSpaceBloc>()
.add(SelectBookableSpace(selectedRoom));
},
);
},
),
),
Expanded(
child: BlocBuilder<DateSelectionBloc, DateSelectionState>(
builder: (context, dateState) {
return CustomCalendarPage(
selectedDate: dateState.selectedDate,
onDateChanged: (day, month, year) {
final newDate = DateTime(year, month, day);
context
.read<DateSelectionBloc>()
.add(SelectDate(newDate));
context
.read<DateSelectionBloc>()
.add(SelectDateFromSidebarCalendar(newDate));
},
);
},
),
),
],
),
),
),
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {},
),
const SizedBox(width: 20),
SvgTextButton(
svgAsset: Assets.groupIcon,
label: 'Manage Users',
onPressed: () {},
),
],
),
BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, state) {
final weekStart = state.weekStart;
final weekEnd =
weekStart.add(const Duration(days: 6));
return WeekNavigation(
weekStart: weekStart,
weekEnd: weekEnd,
onPreviousWeek: () {
context
.read<DateSelectionBloc>()
.add(PreviousWeek());
},
onNextWeek: () {
context
.read<DateSelectionBloc>()
.add(NextWeek());
},
);
SvgTextButton(
svgAsset: Assets.homeIcon,
label: 'Manage Bookable Spaces',
onPressed: () {
widget.pageController!.jumpToPage(2);
},
),
const SizedBox(width: 20),
SvgTextButton(
svgAsset: Assets.groupIcon,
label: 'Manage Users',
onPressed: () {},
),
],
),
Expanded(
flex: 5,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, roomState) {
final selectedRoom =
roomState.selectedBookableSpace;
return BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return BlocListener<CalendarEventsBloc,
CalendarEventState>(
listenWhen: (prev, curr) =>
curr is EventsLoaded,
listener: (context, state) {
if (state is EventsLoaded) {
_eventController
.removeWhere((_) => true);
_eventController.addAll(state.events);
}
},
child: WeeklyCalendarPage(
startTime: selectedRoom
?.bookableConfig.startTime,
endTime: selectedRoom
?.bookableConfig.endTime,
weekStart: dateState.weekStart,
selectedDate: dateState.selectedDate,
eventController: _eventController,
selectedDateFromSideBarCalender: context
.watch<DateSelectionBloc>()
.state
.selectedDateFromSideBarCalender,
),
BlocBuilder<DateSelectionBloc, DateSelectionState>(
builder: (context, state) {
final weekStart = state.weekStart;
final weekEnd = weekStart.add(const Duration(days: 6));
return WeekNavigation(
weekStart: weekStart,
weekEnd: weekEnd,
onPreviousWeek: () {
context
.read<DateSelectionBloc>()
.add(PreviousWeek());
},
onNextWeek: () {
context.read<DateSelectionBloc>().add(NextWeek());
},
);
},
),
],
),
const SizedBox(height: 20),
Expanded(
flex: 5,
child: BlocBuilder<SelectedBookableSpaceBloc,
SelectedBookableSpaceState>(
builder: (context, roomState) {
final selectedRoom = roomState.selectedBookableSpace;
return BlocBuilder<DateSelectionBloc,
DateSelectionState>(
builder: (context, dateState) {
return BlocBuilder<CalendarEventsBloc,
CalendarEventState>(
builder: (context, eventState) {
return WeeklyCalendarPage(
key: ValueKey(selectedRoom?.uuid ?? 'no-room'),
startTime:
selectedRoom?.bookableConfig.startTime,
endTime: selectedRoom?.bookableConfig.endTime,
weekStart: dateState.weekStart,
selectedDate: dateState.selectedDate,
eventController: _eventController,
selectedDateFromSideBarCalender: context
.watch<DateSelectionBloc>()
.state
.selectedDateFromSideBarCalender,
);
},
);
},
),
),
],
);
},
),
),
),
],
),
],
),
),
),
],
),
),
),

View File

@ -76,21 +76,29 @@ class __SidebarContentState extends State<_SidebarContent> {
builder: (context, state) {
return Column(
children: [
const _SidebarHeader(title: 'Spaces'),
Padding(
padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
child: Container(
decoration: BoxDecoration(
color: ColorsManager.white,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
],
),
child: _SidebarHeader(title: 'Spaces')),
),
Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(8.0),
color: ColorsManager.white,
boxShadow: [
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, -2),
blurRadius: 4,
spreadRadius: 0,
),
BoxShadow(
color: ColorsManager.blackColor.withOpacity(0.1),
offset: const Offset(0, 2),
offset: const Offset(0, 4),
blurRadius: 4,
spreadRadius: 0,
),
@ -100,8 +108,8 @@ class __SidebarContentState extends State<_SidebarContent> {
padding: const EdgeInsets.all(8.0),
child: Container(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 8.0),
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Container(
decoration: BoxDecoration(
color: ColorsManager.counterBackgroundColor,
@ -111,12 +119,10 @@ class __SidebarContentState extends State<_SidebarContent> {
children: [
Expanded(
child: TextField(
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.blackColor,
),
style:
Theme.of(context).textTheme.bodyMedium?.copyWith(
color: ColorsManager.blackColor,
),
controller: searchController,
onChanged: _handleSearch,
decoration: InputDecoration(
@ -166,8 +172,7 @@ class __SidebarContentState extends State<_SidebarContent> {
Expanded(
child: ListView.builder(
controller: _scrollController,
itemCount:
state.displayedRooms.length + (state.hasMore ? 1 : 0),
itemCount: state.displayedRooms.length + (state.hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == state.displayedRooms.length) {
return _buildLoadMoreIndicator(state);
@ -178,9 +183,7 @@ class __SidebarContentState extends State<_SidebarContent> {
room: room,
isSelected: state.selectedRoomId == room.uuid,
onTap: () {
context
.read<SidebarBloc>()
.add(SelectRoomEvent(room.uuid));
context.read<SidebarBloc>().add(SelectRoomEvent(room.uuid));
widget.onRoomSelected(room);
},
);
@ -220,7 +223,7 @@ class _SidebarHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
padding: const EdgeInsets.all(10.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View File

@ -66,18 +66,28 @@ class _CustomCalendarPageState extends State<CustomCalendarPage> {
weekdayLabels: const ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
);
return CalendarDatePicker2(
config: config,
value: [_selectedDate],
onValueChanged: (dates) {
final picked = dates.first;
if (picked != null) {
setState(() {
_selectedDate = picked;
});
widget.onDateChanged(picked.day, picked.month, picked.year);
}
},
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(
color: ColorsManager.textGray,
width: 1.0,
),
),
),
child: CalendarDatePicker2(
config: config,
value: [_selectedDate],
onValueChanged: (dates) {
final picked = dates.first;
if (picked != null) {
setState(() {
_selectedDate = picked;
});
widget.onDateChanged(picked.day, picked.month, picked.year);
}
},
),
);
}
}

View File

@ -32,15 +32,17 @@ class EventTileWidget extends StatelessWidget {
return Expanded(
child: Container(
width: double.infinity,
padding: EdgeInsets.all(5),
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: isEventEnded
? ColorsManager.grayColor.withOpacity(0.1)
: ColorsManager.blue1.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
border: const Border(
border: Border(
left: BorderSide(
color: ColorsManager.grayColor,
color: isEventEnded
? ColorsManager.grayColor
: ColorsManager.secondaryColor,
width: 4,
),
),

View File

@ -21,7 +21,7 @@ class RoomListItem extends StatelessWidget {
groupValue: isSelected ? room.uuid : null,
visualDensity: const VisualDensity(vertical: -4),
onChanged: (value) => onTap(),
activeColor: ColorsManager.primaryColor,
activeColor: ColorsManager.secondaryColor,
title: Text(
room.spaceName,
maxLines: 2,

View File

@ -14,26 +14,33 @@ class WeekDayHeader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
DateFormat('EEE').format(date).toUpperCase(),
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 14,
color: isSelectedDay ? Colors.blue : Colors.black,
return ColoredBox(
color: isSelectedDay
? ColorsManager.secondaryColor.withOpacity(0.1)
: Colors.transparent,
child: Column(
children: [
const SizedBox(
height: 10,
),
),
Text(
DateFormat('d').format(date),
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 20,
color:
isSelectedDay ? ColorsManager.blue1 : ColorsManager.blackColor,
Text(
DateFormat('EEE').format(date).toUpperCase(),
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 14,
color: ColorsManager.blackColor,
),
),
),
],
Text(
DateFormat('d').format(date),
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 30,
color: ColorsManager.blackColor,
),
),
],
),
);
}
}

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

@ -24,7 +24,7 @@ class WeeklyCalendarPage extends StatelessWidget {
});
static const double timeLineWidth = 65;
static const int totalDays = 7;
static const double dayColumnWidth = 220;
static const double dayColumnWidth = 220;
final double calendarContentWidth =
timeLineWidth + (totalDays * dayColumnWidth);
@ -57,13 +57,10 @@ class WeeklyCalendarPage extends StatelessWidget {
);
}
const double timeLineWidth = 65;
const double timeLineWidth = 90;
return LayoutBuilder(
builder: (context, constraints) {
bool isInRange(DateTime date, DateTime start, DateTime end) {
!date.isBefore(start) && !date.isAfter(end);
// remove this line and Check if the date is within the range
@ -100,7 +97,6 @@ class WeeklyCalendarPage extends StatelessWidget {
width: width,
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.13),
borderRadius: BorderRadius.circular(8),
),
);
} else if (isSelected) {
@ -110,7 +106,6 @@ class WeeklyCalendarPage extends StatelessWidget {
decoration: BoxDecoration(
color:
ColorsManager.spaceColor.withOpacity(0.07),
borderRadius: BorderRadius.circular(8),
),
);
}
@ -143,7 +138,7 @@ class WeeklyCalendarPage extends StatelessWidget {
heightPerMinute: 1.7,
showLiveTimeLineInAllDays: false,
showVerticalLines: true,
emulateVerticalOffsetBy: -80,
emulateVerticalOffsetBy: -95,
startDay: WeekDays.monday,
liveTimeIndicatorSettings:
const LiveTimeIndicatorSettings(
@ -161,7 +156,7 @@ class WeeklyCalendarPage extends StatelessWidget {
},
timeLineWidth: timeLineWidth,
weekPageHeaderBuilder: (start, end) => Container(),
weekTitleHeight: 60,
weekTitleHeight: 90,
weekNumberBuilder: (firstDayOfWeek) => Padding(
padding: const EdgeInsets.only(right: 15, bottom: 10),
child: Column(
@ -208,8 +203,6 @@ class WeeklyCalendarPage extends StatelessWidget {
},
);
}
}
bool isSameDay(DateTime d1, DateTime d2) {

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

@ -1,6 +1,6 @@
class NonBookableSpacesParams {
final int currentPage;
String? searchedWords;
final String? searchedWords;
NonBookableSpacesParams({
required this.currentPage,
this.searchedWords,

View File

@ -10,51 +10,123 @@ part 'setup_bookable_spaces_state.dart';
class SetupBookableSpacesBloc
extends Bloc<SetupBookableSpacesEvent, SetupBookableSpacesState> {
NonBookableSpacesService nonBookableSpacesService;
List<BookableSpacemodel> selectedBookableSpaces = [];
SetupBookableSpacesBloc(this.nonBookableSpacesService)
: super(SetupBookableSpacesInitial()) {
: super(const SetupBookableSpacesInitial(bookableSpaces: [])) {
on<AddToBookableSpaceEvent>(_onAddToBookableSpaceEvent);
on<RemoveFromBookableSpaceEvent>(_onRemoveFromBookableSpaceEvent);
on<AddBookableDaysEvent>(_onAddBookableDays);
on<ChangeStartTimeEvent>(_onChangeStartTimeEvent);
on<ChangeEndTimeEvent>(_onChangeEndTimeEvent);
on<ChangeCostEvent>(_onChangeCostEvent);
on<CheckConfigurValidityEvent>(_onCheckConfigurValidityEvent);
on<EditModeSelected>(_onEditModeSelected);
}
TimeOfDay? get endTime =>
selectedBookableSpaces.first.spaceConfig!.bookingEndTime;
TimeOfDay? get startTime =>
selectedBookableSpaces.first.spaceConfig!.bookingStartTime;
List<BookableSpacemodel> get currentBookableSpaces {
return switch (state) {
AddNonBookableSpaceIntoBookableState(:final bookableSpaces) =>
bookableSpaces,
RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) =>
bookableSpaces,
SetupBookableSpacesInitial(:final bookableSpaces) => bookableSpaces,
InProgressState(:final bookableSpaces) => bookableSpaces,
ValidSaveButtonState(:final bookableSpaces) => bookableSpaces,
UnValidSaveButtonState(:final bookableSpaces) => bookableSpaces,
};
}
void _onAddToBookableSpaceEvent(
AddToBookableSpaceEvent event,
Emitter<SetupBookableSpacesState> emit,
) {
emit(InProgressState());
selectedBookableSpaces.add(event.nonBookableSpace);
emit(AddNonBookableSpaceIntoBookableState(
bookableSpaces: selectedBookableSpaces));
emit(InProgressState(bookableSpaces: state.bookableSpaces));
final updatedSpaces = List<BookableSpacemodel>.from(state.bookableSpaces);
updatedSpaces.add(event.nonBookableSpace);
emit(AddNonBookableSpaceIntoBookableState(bookableSpaces: updatedSpaces));
}
void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event,
Emitter<SetupBookableSpacesState> emit) {
emit(InProgressState());
selectedBookableSpaces.remove(event.bookableSpace);
emit(InProgressState(bookableSpaces: state.bookableSpaces));
state.bookableSpaces.remove(event.bookableSpace);
emit(RemoveBookableSpaceIntoNonBookableState(
bookableSpaces: selectedBookableSpaces));
bookableSpaces: state.bookableSpaces));
}
void _onAddBookableDays(
AddBookableDaysEvent event, Emitter<SetupBookableSpacesState> emit) {
final updatedSpaces = state.bookableSpaces.map((space) {
final updatedConfig = space.spaceConfig?.copyWith(
bookableDays: event.bookableDays,
);
return space.copyWith(spaceConfig: updatedConfig);
}).toList();
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
}
void _onChangeStartTimeEvent(
ChangeStartTimeEvent event, Emitter<SetupBookableSpacesState> emit) {
final updatedSpaces = state.bookableSpaces.map((space) {
final updatedConfig = space.spaceConfig?.copyWith(
bookingStartTime: event.startTime,
);
return space.copyWith(spaceConfig: updatedConfig);
}).toList();
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
}
void _onChangeEndTimeEvent(
ChangeEndTimeEvent event, Emitter<SetupBookableSpacesState> emit) {
final updatedSpaces = state.bookableSpaces.map((space) {
final updatedConfig = space.spaceConfig?.copyWith(
bookingEndTime: event.endTime,
);
return space.copyWith(spaceConfig: updatedConfig);
}).toList();
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
}
void _onChangeCostEvent(
ChangeCostEvent event, Emitter<SetupBookableSpacesState> emit) {
final updatedSpaces = state.bookableSpaces.map((space) {
final updatedConfig = space.spaceConfig?.copyWith(
cost: event.cost,
);
return space.copyWith(spaceConfig: updatedConfig);
}).toList();
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
}
void _onCheckConfigurValidityEvent(CheckConfigurValidityEvent event,
Emitter<SetupBookableSpacesState> emit) {
if (selectedBookableSpaces.first.spaceConfig!.isValid) {
emit(ValidSaveButtonState());
if (state.bookableSpaces.first.spaceConfig!.isValid) {
emit(ValidSaveButtonState(
bookableSpaces: state.bookableSpaces,
));
} else {
emit(UnValidSaveButtonState());
emit(UnValidSaveButtonState(
bookableSpaces: state.bookableSpaces,
));
}
}
void _onEditModeSelected(
EditModeSelected event, Emitter<SetupBookableSpacesState> emit) {
selectedBookableSpaces.clear();
selectedBookableSpaces.add(event.editingBookableSpace);
EditModeSelected event,
Emitter<SetupBookableSpacesState> emit,
) {
final updatedList = [event.editingBookableSpace];
emit(SetupBookableSpacesInitial(bookableSpaces: updatedList));
}
}

View File

@ -21,6 +21,34 @@ class RemoveFromBookableSpaceEvent extends SetupBookableSpacesEvent {
});
}
class AddBookableDaysEvent extends SetupBookableSpacesEvent {
final List<String> bookableDays;
const AddBookableDaysEvent({
required this.bookableDays,
});
}
class ChangeCostEvent extends SetupBookableSpacesEvent {
final int cost;
const ChangeCostEvent({
required this.cost,
});
}
class ChangeStartTimeEvent extends SetupBookableSpacesEvent {
final TimeOfDay startTime;
const ChangeStartTimeEvent({
required this.startTime,
});
}
class ChangeEndTimeEvent extends SetupBookableSpacesEvent {
final TimeOfDay endTime;
const ChangeEndTimeEvent({
required this.endTime,
});
}
class CheckConfigurValidityEvent extends SetupBookableSpacesEvent {}
class EditModeSelected extends SetupBookableSpacesEvent {

View File

@ -1,32 +1,37 @@
part of 'setup_bookable_spaces_bloc.dart';
sealed class SetupBookableSpacesState extends Equatable {
const SetupBookableSpacesState();
final List<BookableSpacemodel> bookableSpaces;
const SetupBookableSpacesState({required this.bookableSpaces});
TimeOfDay? get startTime =>
bookableSpaces.first.spaceConfig!.bookingStartTime;
TimeOfDay? get endTime => bookableSpaces.first.spaceConfig!.bookingEndTime;
@override
List<Object> get props => [];
}
final class SetupBookableSpacesInitial extends SetupBookableSpacesState {}
final class SetupBookableSpacesInitial extends SetupBookableSpacesState {
const SetupBookableSpacesInitial({required super.bookableSpaces});
}
class AddNonBookableSpaceIntoBookableState extends SetupBookableSpacesState {
final List<BookableSpacemodel> bookableSpaces;
const AddNonBookableSpaceIntoBookableState({
required this.bookableSpaces,
});
const AddNonBookableSpaceIntoBookableState({required super.bookableSpaces});
}
class InProgressState extends SetupBookableSpacesState {}
class InProgressState extends SetupBookableSpacesState {
const InProgressState({required super.bookableSpaces});
}
class RemoveBookableSpaceIntoNonBookableState extends SetupBookableSpacesState {
final List<BookableSpacemodel> bookableSpaces;
const RemoveBookableSpaceIntoNonBookableState({
required this.bookableSpaces,
required super.bookableSpaces,
});
}
class ValidSaveButtonState extends SetupBookableSpacesState {}
class UnValidSaveButtonState extends SetupBookableSpacesState {}
class ValidSaveButtonState extends SetupBookableSpacesState {
const ValidSaveButtonState({required super.bookableSpaces});
}
class UnValidSaveButtonState extends SetupBookableSpacesState {
const UnValidSaveButtonState({required super.bookableSpaces});
}

View File

@ -52,20 +52,20 @@ class _SetupBookableSpacesDialogState extends State<SetupBookableSpacesDialog> {
),
)..add(
LoadUnBookableSpacesEvent(
nonBookableSpacesParams:
NonBookableSpacesParams(currentPage: 1),
nonBookableSpacesParams: NonBookableSpacesParams(currentPage: 1),
),
),
),
BlocProvider<SetupBookableSpacesBloc>(
create: widget.editingBookableSpace == null
? (context) => SetupBookableSpacesBloc(
RemoteNonBookableSpaces(HTTPService()))
: (context) => SetupBookableSpacesBloc(
RemoteNonBookableSpaces(HTTPService()))
..add(EditModeSelected(
editingBookableSpace: widget.editingBookableSpace!,
)),
? (context) =>
SetupBookableSpacesBloc(RemoteNonBookableSpaces(HTTPService()))
: (context) =>
SetupBookableSpacesBloc(RemoteNonBookableSpaces(HTTPService()))
..add(
EditModeSelected(
editingBookableSpace: widget.editingBookableSpace!),
),
),
BlocProvider(
create: (context) => SendBookableSpacesBloc(
@ -74,7 +74,7 @@ class _SetupBookableSpacesDialogState extends State<SetupBookableSpacesDialog> {
)
],
child: AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
title: Center(
child: Text(
@ -115,16 +115,14 @@ class _SetupBookableSpacesDialogState extends State<SetupBookableSpacesDialog> {
),
Builder(builder: (context) {
final stepsState = context.watch<StepsCubit>().state;
final setupBookableSpacesBloc =
context.watch<SetupBookableSpacesBloc>();
final selectedSpaces =
setupBookableSpacesBloc.selectedBookableSpaces;
final bookableSpaces =
context.watch<SetupBookableSpacesBloc>().state.bookableSpaces;
return stepsState is StepOneState
? NextFirstStepButton(selectedSpaces: selectedSpaces)
? const NextFirstStepButton()
: SaveSecondStepButton(
selectedSpaces: selectedSpaces,
pointsController: pointsController,
isEditingMode: widget.editingBookableSpace != null,
bookableSpaces: bookableSpaces,
);
}),
],

View File

@ -21,16 +21,14 @@ class BookableSpaceSwitchActivationWidget extends StatelessWidget {
return Center(
child: Transform.scale(
scale: 0.7,
child:
BlocConsumer<UpdateBookableSpacesBloc, UpdateBookableSpacesState>(
child: BlocConsumer<UpdateBookableSpacesBloc, UpdateBookableSpacesState>(
listener: (context, updateState) {
if (updateState is UpdateBookableSpaceSuccess) {
context.read<BookableSpacesBloc>().add(
InsertUpdatedSpaceEvent(
bookableSpaces: bookableSpaces,
bookableSpace: space,
updatedBookableSpaceConfig:
updateState.bookableSpaceConfig,
updatedBookableSpaceConfig: updateState.bookableSpaceConfig,
),
);
}
@ -42,16 +40,16 @@ class BookableSpaceSwitchActivationWidget extends StatelessWidget {
return const Center(child: CircularProgressIndicator());
}
return Switch(
trackOutlineColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return ColorsManager.whiteColors;
trackOutlineColor:
WidgetStateProperty.resolveWith<Color>((Set<WidgetState> states) {
return ColorsManager.white;
}),
value: space.spaceConfig!.availability,
activeTrackColor: ColorsManager.blueColor,
activeTrackColor: ColorsManager.dialogBlueTitle,
inactiveTrackColor: ColorsManager.grayBorder,
thumbColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return ColorsManager.whiteColors;
thumbColor:
WidgetStateProperty.resolveWith<Color>((Set<WidgetState> states) {
return ColorsManager.white;
}),
onChanged: (value) {
context.read<UpdateBookableSpacesBloc>().add(

View File

@ -17,6 +17,8 @@ class BookingPeriodWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final state = context.watch<SetupBookableSpacesBloc>().state;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -32,122 +34,108 @@ class BookingPeriodWidget extends StatelessWidget {
const Text('Booking Period'),
],
),
const SizedBox(height: 5),
Container(
width: 300,
width: 230,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(8),
color: ColorsManager.circleRolesBackground,
boxShadow: [
BoxShadow(
offset: Offset.zero,
blurRadius: 4,
spreadRadius: 0,
color: ColorsManager.timePickerColor.withValues(alpha: 0.15),
)
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TimePickerWidget(
title: editingBookableSpace == null
? 'Start Time'
: editingBookableSpace!.spaceConfig!.bookingStartTime!
.format(context),
onTimePicked: (timePicked) {
if (timePicked == null) {
title: editingBookableSpace?.spaceConfig?.bookingStartTime
?.format(context) ??
'Start Time',
onTimePicked: (pickedStartTime) {
if (pickedStartTime == null) return;
if (state.endTime != null &&
isEndTimeAfterStartTime(
pickedStartTime, state.endTime!)) {
_showInvalidSnackBar(
context, "You can't choose Start Time after End Time");
return;
}
final setupBookableSpacesBloc =
context.read<SetupBookableSpacesBloc>();
if (setupBookableSpacesBloc.endTime != null &&
isEndTimeAfterStartTime(
timePicked, setupBookableSpacesBloc.endTime!)) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content:
Text("You can't choose start Time Before End time"),
duration: Duration(seconds: 2),
backgroundColor: ColorsManager.red,
));
throw Exception();
} else {
for (int i = 0;
i <
setupBookableSpacesBloc
.selectedBookableSpaces.length;
i++) {
final space =
setupBookableSpacesBloc.selectedBookableSpaces[i];
final updatedConfig = space.spaceConfig
?.copyWith(bookingStartTime: timePicked);
final updatedSpace =
space.copyWith(spaceConfig: updatedConfig);
setupBookableSpacesBloc.selectedBookableSpaces[i] =
updatedSpace;
}
}
context.read<SetupBookableSpacesBloc>().add(
ChangeStartTimeEvent(startTime: pickedStartTime),
);
context.read<SetupBookableSpacesBloc>().add(
CheckConfigurValidityEvent(),
);
},
),
const SizedBox(width: 10),
const Icon(
Icons.arrow_right_alt,
color: ColorsManager.grayColor,
size: 13,
),
TimePickerWidget(
title: editingBookableSpace == null
? 'End Time'
: editingBookableSpace!.spaceConfig!.bookingEndTime!
.format(context),
onTimePicked: (timePicked) {
if (timePicked == null) {
title: editingBookableSpace?.spaceConfig?.bookingEndTime
?.format(context) ??
'End Time',
onTimePicked: (pickedEndTime) {
if (pickedEndTime == null) return;
if (state.startTime != null &&
isEndTimeAfterStartTime(
state.startTime!, pickedEndTime)) {
_showInvalidSnackBar(
context, "You can't choose End Time before Start Time");
return;
}
final setupBookableSpacesBloc =
context.read<SetupBookableSpacesBloc>();
if (setupBookableSpacesBloc.startTime != null &&
isEndTimeAfterStartTime(
setupBookableSpacesBloc.startTime!, timePicked)) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content:
Text("You can't choose End Time After Start time"),
duration: Duration(seconds: 2),
backgroundColor: ColorsManager.red,
));
throw Exception();
} else {
for (int i = 0;
i <
setupBookableSpacesBloc
.selectedBookableSpaces.length;
i++) {
final space =
setupBookableSpacesBloc.selectedBookableSpaces[i];
final updatedConfig = space.spaceConfig
?.copyWith(bookingEndTime: timePicked);
final updatedSpace =
space.copyWith(spaceConfig: updatedConfig);
setupBookableSpacesBloc.selectedBookableSpaces[i] =
updatedSpace;
}
}
context.read<SetupBookableSpacesBloc>().add(
ChangeEndTimeEvent(endTime: pickedEndTime),
);
context.read<SetupBookableSpacesBloc>().add(
CheckConfigurValidityEvent(),
);
},
),
const SizedBox(width: 15),
Container(
width: 50,
width: 30,
height: 32,
alignment: Alignment.center,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10),
),
),
alignment: Alignment.center,
child: SvgPicture.asset(
Assets.clockIcon,
height: 15,
height: 18,
color: ColorsManager.blackColor.withValues(alpha: 0.4),
),
)
),
],
),
),
],
);
}
void _showInvalidSnackBar(BuildContext context, String message) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
backgroundColor: ColorsManager.red,
),
);
}
}

View File

@ -2,16 +2,15 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CheckBoxSpaceWidget extends StatelessWidget {
final BookableSpacemodel nonBookableSpace;
final List<BookableSpacemodel> selectedSpaces;
const CheckBoxSpaceWidget({
super.key,
required this.nonBookableSpace,
required this.selectedSpaces,
});
@override
@ -32,22 +31,25 @@ class CheckBoxSpaceWidget extends StatelessWidget {
_ => false,
};
return Checkbox(
return CustomCheckboxWidget(
value: isChecked,
onChanged: (value) {
final bloc = context.read<SetupBookableSpacesBloc>();
if (value ?? false) {
bloc.add(AddToBookableSpaceEvent(
nonBookableSpace: nonBookableSpace));
nonBookableSpace: nonBookableSpace,
));
} else {
bloc.add(RemoveFromBookableSpaceEvent(
bookableSpace: nonBookableSpace));
bookableSpace: nonBookableSpace,
));
}
},
);
},
),
const SizedBox(width: 5),
const SizedBox(width: 15),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -55,15 +57,17 @@ class CheckBoxSpaceWidget extends StatelessWidget {
Text(
nonBookableSpace.spaceName,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: ColorsManager.textGray,
fontWeight: FontWeight.w700,
fontSize: 12,
color: ColorsManager.titleGray,
),
),
Text(
nonBookableSpace.spaceVirtualAddress,
style: const TextStyle(
fontSize: 12,
color: ColorsManager.textGray,
fontWeight: FontWeight.w400,
fontSize: 10,
color: ColorsManager.titleGray,
),
),
],

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CustomCheckboxWidget extends StatelessWidget {
final bool value;
final ValueChanged<bool?> onChanged;
final double? outHeight;
final double? outWidth;
final double? iconSize;
const CustomCheckboxWidget({
super.key,
required this.value,
required this.onChanged,
this.outWidth,
this.outHeight,
this.iconSize,
});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onChanged(!value),
child: Container(
width: outWidth ?? 17,
height: outHeight ?? 17,
decoration: BoxDecoration(
color: value ? Colors.white : ColorsManager.checkBoxFillColor,
border: value
? Border.all(color: ColorsManager.secondaryColor, width: 1)
: Border.all(color: ColorsManager.checkBoxBorderGray, width: 1),
borderRadius: BorderRadius.circular(4),
),
child: value
? Center(
child: Container(
width: outWidth != null ? outWidth! - 4 : 13,
height: outHeight != null ? outHeight! - 4 : 13,
decoration: BoxDecoration(
color: ColorsManager.secondaryColor,
borderRadius: BorderRadius.circular(2),
),
child: const Icon(
Icons.check,
size: 12,
color: Colors.white,
),
),
)
: null,
),
);
}
}

View File

@ -20,38 +20,53 @@ class EditBookableSpaceButtonWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
final bookableBloc = context.read<BookableSpacesBloc>();
showDialog(
context: context,
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: bookableBloc,
),
BlocProvider(
create: (context) => UpdateBookableSpacesBloc(
RemoteUpdateBookableSpaceService(HTTPService()),
),
),
],
child: SetupBookableSpacesDialog(
editingBookableSpace: space,
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
offset: Offset.zero,
blurRadius: 3,
spreadRadius: 0,
color: ColorsManager.timePickerColor.withValues(
alpha: 0.3,
),
),
);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
fixedSize: const Size(50, 30),
elevation: 1,
)
],
),
child: SvgPicture.asset(
Assets.settings,
height: 15,
color: ColorsManager.blue1,
child: ElevatedButton(
onPressed: () {
final bookableBloc = context.read<BookableSpacesBloc>();
showDialog(
context: context,
builder: (context) => MultiBlocProvider(
providers: [
BlocProvider.value(
value: bookableBloc,
),
BlocProvider(
create: (context) => UpdateBookableSpacesBloc(
RemoteUpdateBookableSpaceService(HTTPService()),
),
),
],
child: SetupBookableSpacesDialog(
editingBookableSpace: space,
),
),
);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size(45, 30),
elevation: 0,
),
child: SvgPicture.asset(
Assets.settings,
height: 13,
color: ColorsManager.blue1,
),
),
),
);

View File

@ -66,18 +66,19 @@ class PaginationButtonsWidget extends StatelessWidget {
height: 30,
alignment: Alignment.center,
decoration: BoxDecoration(
color: i == currentPage
? ColorsManager.dialogBlueTitle
: Colors.grey[300],
borderRadius: BorderRadius.circular(8),
),
color: i == currentPage
? ColorsManager.dialogBlueTitle
: ColorsManager.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: ColorsManager.lightGrayBorderColor,
)),
child: Text(
'$i',
style: TextStyle(
color: i == currentPage ? Colors.white : Colors.black,
fontWeight: i == currentPage
? FontWeight.bold
: FontWeight.normal,
fontWeight:
i == currentPage ? FontWeight.bold : FontWeight.normal,
),
),
),
@ -127,18 +128,21 @@ class PaginationButtonsWidget extends StatelessWidget {
);
}
Widget _buildArrowButton(
{required String label, required VoidCallback onTap}) {
Widget _buildArrowButton({required String label, required VoidCallback onTap}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
width: 30,
height: 30,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.grey[300],
borderRadius: BorderRadius.circular(8),
),
color: ColorsManager.white,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: ColorsManager.lightGrayBorderColor,
)),
child: Text(
label,
style: const TextStyle(

View File

@ -24,20 +24,37 @@ class RowOfButtonsTitleWidget extends StatelessWidget {
children: [
Row(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
offset: Offset.zero,
blurRadius: 3,
spreadRadius: 0,
color: ColorsManager.timePickerColor.withValues(
alpha: 0.3,
),
)
],
),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size(50, 40),
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
child: SvgPicture.asset(
Assets.backButtonIcon,
height: 15,
),
onPressed: () {
pageController.jumpToPage(1);
}),
child: SvgPicture.asset(
Assets.backButtonIcon,
height: 15,
),
onPressed: () {
pageController.jumpToPage(1);
}),
),
const SizedBox(
width: 10,
),

View File

@ -1,32 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart';
class NextFirstStepButton extends StatelessWidget {
final List<BookableSpacemodel> selectedSpaces;
const NextFirstStepButton({
super.key,
required this.selectedSpaces,
});
@override
Widget build(BuildContext context) {
return ButtonsDividerBottomDialogWidget(
title: 'Next',
onNextPressed: selectedSpaces.isEmpty
? null
: () {
context.read<StepsCubit>().goToNextStep();
context
.read<SetupBookableSpacesBloc>()
.add(CheckConfigurValidityEvent());
},
onCancelPressed: () => context.pop(),
return BlocBuilder<SetupBookableSpacesBloc, SetupBookableSpacesState>(
builder: (context, state) {
return switch (state) {
SetupBookableSpacesInitial() => ButtonsDividerBottomDialogWidget(
title: 'Next',
onNextPressed: null,
onCancelPressed: () => context.pop(),
),
AddNonBookableSpaceIntoBookableState(:final bookableSpaces) ||
RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) =>
ButtonsDividerBottomDialogWidget(
title: 'Next',
onNextPressed: bookableSpaces.isEmpty
? null
: () {
context.read<StepsCubit>().goToNextStep();
context.read<SetupBookableSpacesBloc>().add(
CheckConfigurValidityEvent(),
);
},
onCancelPressed: () => context.pop(),
),
_ => const SizedBox(),
};
},
);
}
}

View File

@ -9,14 +9,14 @@ import 'package:syncrow_web/utils/color_manager.dart';
class PointsPartWidget extends StatefulWidget {
final BookableSpacemodel? editingBookableSpace;
final TextEditingController pointsController;
const PointsPartWidget({
super.key,
required this.pointsController,
this.editingBookableSpace,
});
final TextEditingController pointsController;
@override
State<PointsPartWidget> createState() => _PointsPartWidgetState();
}
@ -24,26 +24,28 @@ class PointsPartWidget extends StatefulWidget {
class _PointsPartWidgetState extends State<PointsPartWidget> {
@override
void initState() {
super.initState();
if (widget.editingBookableSpace != null) {
widget.pointsController.text =
widget.editingBookableSpace!.spaceConfig!.cost.toString();
}
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<TogglePointsSwitchCubit, TogglePointsSwitchState>(
builder: (context, state) {
builder: (context, switchState) {
final isSwitchOn = switchState is ActivatePointsSwitch;
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
if (state is ActivatePointsSwitch)
if (isSwitchOn)
Text(
'* ',
style: Theme.of(context)
@ -52,87 +54,59 @@ class _PointsPartWidgetState extends State<PointsPartWidget> {
.copyWith(color: Colors.red),
)
else
const SizedBox(
width: 11,
),
const SizedBox(width: 11),
const Text('Points/hrs'),
],
),
Transform.scale(
scale: 0.7,
child: Switch(
trackOutlineColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return ColorsManager.whiteColors;
}),
activeTrackColor: ColorsManager.blueColor,
inactiveTrackColor: ColorsManager.grayBorder,
thumbColor: WidgetStateProperty.resolveWith<Color>(
(Set<WidgetState> states) {
return ColorsManager.whiteColors;
}),
value: state is ActivatePointsSwitch,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
trackOutlineColor: WidgetStateProperty.all(ColorsManager.white),
activeTrackColor: ColorsManager.dialogBlueTitle,
inactiveTrackColor: ColorsManager.lightGrayBorderColor,
thumbColor: WidgetStateProperty.all(ColorsManager.white),
value: isSwitchOn,
onChanged: (value) {
final toggleCubit = context.read<TogglePointsSwitchCubit>();
final bloc = context.read<SetupBookableSpacesBloc>();
final updatedCost = value ? -1 : 0;
final switchCubit =
context.read<TogglePointsSwitchCubit>();
if (value) {
switchCubit.activateSwitch();
toggleCubit.activateSwitch();
} else {
switchCubit.unActivateSwitch();
toggleCubit.unActivateSwitch();
widget.pointsController.clear();
}
for (int i = 0;
i < bloc.selectedBookableSpaces.length;
i++) {
final space = bloc.selectedBookableSpaces[i];
final updatedConfig =
space.spaceConfig?.copyWith(cost: updatedCost);
final updatedSpace =
space.copyWith(spaceConfig: updatedConfig);
bloc.selectedBookableSpaces[i] = updatedSpace;
}
bloc.add(ChangeCostEvent(cost: updatedCost));
bloc.add(CheckConfigurValidityEvent());
},
),
)
),
],
),
const SizedBox(
height: 5,
),
if (state is ActivatePointsSwitch)
const SizedBox(height: 5),
if (isSwitchOn)
SearchUnbookableSpacesWidget(
title: 'Ex: 0',
height: 40,
onChanged: (p0) {
topPadding: 0,
blur: 1,
raduis: 10,
height: 34,
controller: widget.pointsController,
suffix: const SizedBox(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (_) {
final updatedCost =
int.tryParse(widget.pointsController.text) ?? 0;
final bloc = context.read<SetupBookableSpacesBloc>();
for (var i = 0; i < bloc.selectedBookableSpaces.length; i++) {
final space = bloc.selectedBookableSpaces[i];
final updatedConfig =
space.spaceConfig?.copyWith(cost: updatedCost);
final updatedSpace =
space.copyWith(spaceConfig: updatedConfig);
bloc.selectedBookableSpaces[i] = updatedSpace;
}
context
.read<SetupBookableSpacesBloc>()
.add(CheckConfigurValidityEvent());
.add(ChangeCostEvent(cost: updatedCost));
context.read<SetupBookableSpacesBloc>().add(
CheckConfigurValidityEvent(),
);
},
controller: widget.pointsController,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
suffix: const SizedBox(),
)
else
const SizedBox(),

View File

@ -10,68 +10,68 @@ import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/prese
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart';
class SaveSecondStepButton extends StatelessWidget {
final List<BookableSpacemodel> selectedSpaces;
final TextEditingController pointsController;
final bool isEditingMode;
final List<BookableSpacemodel> bookableSpaces;
const SaveSecondStepButton({
super.key,
required this.selectedSpaces,
required this.pointsController,
required this.isEditingMode,
required this.bookableSpaces,
});
@override
Widget build(BuildContext context) {
return BlocConsumer<SendBookableSpacesBloc, SendBookableSpacesState>(
return BlocListener<SendBookableSpacesBloc, SendBookableSpacesState>(
listener: (context, state) {
if (state is SendBookableSpacesSuccess) {
context.read<NonBookableSpacesBloc>().add(CallInitStateEvent());
} else if (state is SendBookableSpacesError) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.error),
),
SnackBar(content: Text(state.error)),
);
}
},
builder: (context, state) {
return ButtonsDividerBottomDialogWidget(
title: 'Save',
onNextPressed: state is UnValidSaveButtonState
? null
: () {
if (selectedSpaces.any(
(element) => element.isValid,
)) {
isEditingMode
? callEditLogic(context)
: context.read<SendBookableSpacesBloc>().add(
child: BlocBuilder<SetupBookableSpacesBloc, SetupBookableSpacesState>(
builder: (context, state) {
return ButtonsDividerBottomDialogWidget(
title: 'Save',
onNextPressed: state is UnValidSaveButtonState
? null
: () {
if (bookableSpaces.any((e) => e.isValid)) {
if (isEditingMode) {
callEditLogic(context);
} else {
context.read<SendBookableSpacesBloc>().add(
SendBookableSpacesToApi(
selectedBookableSpaces: context
.read<SetupBookableSpacesBloc>()
.selectedBookableSpaces),
selectedBookableSpaces: bookableSpaces,
),
);
}
},
onCancelPressed: () => context.pop(),
);
},
}
}
},
onCancelPressed: () => context.pop(),
);
},
),
);
}
void callEditLogic(BuildContext context) {
context.read<UpdateBookableSpacesBloc>().add(
UpdateBookableSpace(
onSuccess: () =>
context.read<NonBookableSpacesBloc>().add(CallInitStateEvent()),
updatedParam: UpdateBookableSpaceParam.fromBookableModel(
context
.read<SetupBookableSpacesBloc>()
.selectedBookableSpaces
.first,
print(bookableSpaces.first.spaceConfig!.cost);
if (bookableSpaces.isNotEmpty) {
context.read<UpdateBookableSpacesBloc>().add(
UpdateBookableSpace(
onSuccess: () => context
.read<NonBookableSpacesBloc>()
.add(CallInitStateEvent()),
updatedParam: UpdateBookableSpaceParam.fromBookableModel(
bookableSpaces.first,
),
),
),
);
);
}
}
}

View File

@ -1,22 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class SearchUnbookableSpacesWidget extends StatelessWidget {
final String title;
final Widget? suffix;
final double? height;
final double? width;
final double? blur;
final double? raduis;
final double? topPadding;
final TextEditingController? controller;
final List<TextInputFormatter>? inputFormatters;
final void Function(String)? onChanged;
const SearchUnbookableSpacesWidget({
required this.title,
this.controller,
this.blur,
this.onChanged,
this.suffix,
this.height,
this.width,
this.topPadding,
this.raduis,
this.inputFormatters,
super.key,
});
@ -25,16 +33,17 @@ class SearchUnbookableSpacesWidget extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
width: width ?? 480,
height: height ?? 30,
height: height ?? 40,
padding: const EdgeInsets.only(top: 4),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
color: ColorsManager.white,
borderRadius: BorderRadius.circular(raduis ?? 15),
boxShadow: [
BoxShadow(
color: ColorsManager.shadowOfSearchTextfield,
offset: Offset(0, 4),
blurRadius: 5,
color: ColorsManager.shadowOfSearchTextfield.withValues(alpha: 0.15),
offset: Offset.zero,
blurRadius: blur ?? 5,
spreadRadius: 0,
),
],
),
@ -43,14 +52,21 @@ class SearchUnbookableSpacesWidget extends StatelessWidget {
inputFormatters: inputFormatters,
onChanged: onChanged,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 5, horizontal: 15),
contentPadding: EdgeInsets.symmetric(
vertical: topPadding ?? 5,
horizontal: 15,
),
hintText: title,
hintStyle: const TextStyle(color: ColorsManager.hintTextGrey),
border: InputBorder.none,
suffixIcon: suffix ??
const Icon(Icons.search,
size: 20, color: ColorsManager.hintTextGrey),
suffixIcon: Padding(
padding: const EdgeInsets.all(10),
child: suffix ??
SvgPicture.asset(
Assets.searchIcon,
height: 12,
),
),
),
style: const TextStyle(
fontSize: 14,

View File

@ -26,7 +26,7 @@ class StepTwoDetailsWidget extends StatelessWidget {
editingBookableSpace: editingBookableSpace,
),
const SizedBox(
height: 20,
height: 30,
),
BookingPeriodWidget(
editingBookableSpace: editingBookableSpace,

View File

@ -26,14 +26,14 @@ class StepperPartWidget extends StatelessWidget {
Container(
padding: const EdgeInsets.only(left: 3),
alignment: Alignment.centerLeft,
height: 50,
height: 40,
child: const VerticalDivider(
width: 8,
)),
const CircleTitleStepperWidget(
title: 'Settings',
titleColor: ColorsManager.softGray,
circleColor: ColorsManager.whiteColors,
circleColor: ColorsManager.white,
borderColor: ColorsManager.textGray,
)
],
@ -49,7 +49,7 @@ class StepperPartWidget extends StatelessWidget {
titleColor: ColorsManager.softGray,
cicleIcon: Icon(
Icons.check,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
size: 12,
),
circleColor: ColorsManager.trueIconGreen,
@ -59,7 +59,7 @@ class StepperPartWidget extends StatelessWidget {
Container(
padding: const EdgeInsets.only(left: 3),
alignment: Alignment.centerLeft,
height: 50,
height: 40,
child: const VerticalDivider(
width: 8,
)),

View File

@ -12,7 +12,7 @@ class TimePickerWidget extends StatefulWidget {
required this.onTimePicked,
required this.title,
});
late SetupBookableSpacesBloc setupBookableSpacesBloc;
late final SetupBookableSpacesBloc setupBookableSpacesBloc;
final void Function(TimeOfDay? timePicked) onTimePicked;
@override
State<TimePickerWidget> createState() => _TimePickerWidgetState();
@ -47,13 +47,17 @@ class _TimePickerWidgetState extends State<TimePickerWidget> {
);
},
);
if (tempTime == null) return;
widget.onTimePicked(tempTime);
timePicked = tempTime;
widget.setupBookableSpacesBloc.add(CheckConfigurValidityEvent());
setState(() {});
},
child: Container(
width: 100,
height: 32,
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(

View File

@ -36,9 +36,7 @@ class UnbookableListWidget extends StatelessWidget {
if (index < nonBookableSpaces.data.length) {
return CheckBoxSpaceWidget(
nonBookableSpace: nonBookableSpaces.data[index],
selectedSpaces: context
.read<SetupBookableSpacesBloc>()
.selectedBookableSpaces,
);
} else {
return const Padding(

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart';
class WeekDaysCheckboxRow extends StatefulWidget {
final BookableSpacemodel? editingBookableSpace;
@ -24,6 +25,7 @@ class _WeekDaysCheckboxRowState extends State<WeekDaysCheckboxRow> {
'Sat': false,
'Sun': false,
};
@override
void initState() {
super.initState();
@ -40,52 +42,66 @@ class _WeekDaysCheckboxRowState extends State<WeekDaysCheckboxRow> {
@override
Widget build(BuildContext context) {
return Row(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _daysChecked.entries.map((entry) {
return Expanded(
child: Row(
children: [
Expanded(
child: Checkbox(
value: entry.value,
onChanged: (newValue) {
setState(() {
_daysChecked[entry.key] = newValue ?? false;
});
children: [
Row(
children: [
Text(
'* ',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(color: Colors.red),
),
const Text('Days'),
],
),
const SizedBox(height: 20),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: _daysChecked.entries.map((entry) {
return Expanded(
child: Row(
children: [
CustomCheckboxWidget(
outHeight: 16,
outWidth: 16,
value: entry.value,
onChanged: (newValue) {
setState(() {
_daysChecked[entry.key] = newValue ?? false;
});
final selectedDays = _daysChecked.entries
.where((e) => e.value)
.map((e) => e.key)
.toList();
final selectedDays = _daysChecked.entries
.where((e) => e.value)
.map((e) => e.key)
.toList();
final bloc = context.read<SetupBookableSpacesBloc>();
for (int i = 0;
i < bloc.selectedBookableSpaces.length;
i++) {
final space = bloc.selectedBookableSpaces[i];
final updatedConfig = space.spaceConfig
?.copyWith(bookableDays: selectedDays);
final updatedSpace =
space.copyWith(spaceConfig: updatedConfig);
bloc.selectedBookableSpaces[i] = updatedSpace;
}
bloc.add(CheckConfigurValidityEvent());
},
),
context.read<SetupBookableSpacesBloc>().add(
AddBookableDaysEvent(bookableDays: selectedDays),
);
context.read<SetupBookableSpacesBloc>().add(
CheckConfigurValidityEvent(),
);
},
),
const SizedBox(width: 8),
Expanded(
child: Text(
entry.key,
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w400,
),
),
),
],
),
Expanded(
child: Text(
entry.key,
style: const TextStyle(fontSize: 10),
)),
],
),
);
}).toList(),
);
}).toList(),
),
],
);
}
}

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

@ -104,7 +104,7 @@ abstract final class RangeOfAqiChartsHelper {
) {
return LineTouchData(
touchTooltipData: LineTouchTooltipData(
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
getTooltipColor: (touchTooltipItem) => ColorsManager.white,
tooltipBorder: const BorderSide(
color: ColorsManager.semiTransparentBlack,
),

View File

@ -77,7 +77,7 @@ class AqiDistributionChart extends StatelessWidget {
BarTouchData _barTouchData(BuildContext context) {
return BarTouchData(
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (_) => ColorsManager.whiteColors,
getTooltipColor: (_) => ColorsManager.white,
tooltipBorder: const BorderSide(
color: ColorsManager.semiTransparentBlack,
),

View File

@ -65,7 +65,7 @@ class AqiGauge extends StatelessWidget {
pointer: GaugePointer.circle(
position: const GaugePointerPosition.surface(),
radius: MediaQuery.sizeOf(context).width * 0.004,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: GaugePointerBorder(
width: 6,
color: statusColor,

View File

@ -20,7 +20,7 @@ class AqiLocationInfoCell extends StatelessWidget {
return Expanded(
child: Container(
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
borderRadius: BorderRadius.circular(12),
),
child: Stack(

View File

@ -62,7 +62,7 @@ class AqiSubValueWidget extends StatelessWidget {
child: Container(
padding: const EdgeInsetsDirectional.all(10),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
borderRadius: BorderRadius.circular(12),
),
child: Row(

View File

@ -46,7 +46,7 @@ class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
value: widget.selectedAqiType,
isDense: true,
borderRadius: BorderRadius.circular(16),
dropdownColor: ColorsManager.whiteColors,
dropdownColor: ColorsManager.white,
underline: const SizedBox.shrink(),
icon: const RotatedBox(
quarterTurns: 1,

View File

@ -107,7 +107,7 @@ class RangeOfAqiChart extends StatelessWidget {
show: true,
getDotPainter: (_, __, ___, ____) => FlDotCirclePainter(
radius: 2,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
strokeWidth: 2,
strokeColor: color,
),

View File

@ -45,7 +45,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
child: Container(
padding: const EdgeInsetsDirectional.all(20),
width: 320,
@ -108,7 +108,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
style: context.textTheme.titleSmall?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
),
),
@ -217,7 +217,7 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
style: context.textTheme.titleSmall?.copyWith(
fontSize: 12,
color: isSelected
? ColorsManager.whiteColors
? ColorsManager.white
: isFutureMonth
? ColorsManager.blackColor.withValues(alpha: 0.3)
: ColorsManager.blackColor.withValues(alpha: 0.8),

View File

@ -59,7 +59,7 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
: state.communityList;
return Container(
height: MediaQuery.sizeOf(context).height,
decoration: const BoxDecoration(color: ColorsManager.whiteColors),
decoration: const BoxDecoration(color: ColorsManager.white),
child: state is SpaceTreeLoadingState
? const Center(child: CircularProgressIndicator())
: Column(

View File

@ -33,7 +33,7 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
child: Container(
padding: const EdgeInsetsDirectional.all(20),
width: 320,
@ -92,7 +92,7 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
style: context.textTheme.titleSmall?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
),
),
@ -144,7 +144,7 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
style: context.textTheme.titleSmall?.copyWith(
fontSize: 12,
color: isSelected
? ColorsManager.whiteColors
? ColorsManager.white
: ColorsManager.blackColor.withValues(alpha: 0.8),
fontWeight: FontWeight.w500,
),

View File

@ -81,7 +81,7 @@ abstract final class EnergyManagementChartsHelper {
static LineTouchTooltipData lineTouchTooltipData() {
return LineTouchTooltipData(
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
getTooltipColor: (touchTooltipItem) => ColorsManager.white,
tooltipBorder: const BorderSide(color: ColorsManager.semiTransparentBlack),
tooltipRoundedRadius: 16,
showOnTopOfTheChartBoxArea: false,

View File

@ -72,7 +72,7 @@ class AnalyticsDeviceDropdown extends StatelessWidget {
value: state.selectedDevice,
isDense: true,
borderRadius: BorderRadius.circular(16),
dropdownColor: ColorsManager.whiteColors,
dropdownColor: ColorsManager.white,
underline: const SizedBox.shrink(),
icon: const RotatedBox(
quarterTurns: 1,

View File

@ -18,7 +18,6 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
Widget build(BuildContext context) {
return BarChart(
BarChartData(
gridData: EnergyManagementChartsHelper.gridData().copyWith(
checkToShowHorizontalLine: (value) => true,
horizontalInterval: 250,
@ -74,7 +73,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
BarTouchData _barTouchData(BuildContext context) {
return BarTouchData(
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
getTooltipColor: (touchTooltipItem) => ColorsManager.white,
tooltipBorder: const BorderSide(
color: ColorsManager.semiTransparentBlack,
),

View File

@ -34,7 +34,7 @@ class HeatMapTooltip extends StatelessWidget {
style: context.textTheme.bodySmall?.copyWith(
fontSize: 12,
fontWeight: FontWeight.w700,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
),
const Divider(height: 2, thickness: 1),
@ -43,7 +43,7 @@ class HeatMapTooltip extends StatelessWidget {
style: context.textTheme.bodySmall?.copyWith(
fontSize: 10,
fontWeight: FontWeight.w500,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
),
],

View File

@ -64,7 +64,7 @@ class OccupancyChart extends StatelessWidget {
BarTouchData _barTouchData(BuildContext context) {
return BarTouchData(
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
getTooltipColor: (touchTooltipItem) => ColorsManager.white,
tooltipBorder: const BorderSide(
color: ColorsManager.semiTransparentBlack,
),

View File

@ -121,7 +121,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
const Spacer(),
Expanded(
flex: 2,
child: _buildLoginFormFields(context, loginBloc, size),
child:
_buildLoginFormFields(context, loginBloc, size),
),
const Spacer(),
],
@ -147,8 +148,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
child: Form(
key: loginBloc.loginFormKey,
child: Padding(
padding:
EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003),
padding: EdgeInsets.symmetric(
horizontal: size.width * 0.02, vertical: size.width * 0.003),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
@ -303,10 +304,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
decoration: textBoxDecoration()!.copyWith(
errorStyle: const TextStyle(height: 0),
hintText: 'Enter your email address',
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
style: const TextStyle(color: Colors.black),
),
),
@ -338,7 +337,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
controller: loginBloc.loginPasswordController,
onFieldSubmitted: (value) {
if (loginBloc.loginFormKey.currentState!.validate()) {
loginBloc.add(LoginButtonPressed(
loginBloc.add(LoginButtonPressed(
username: loginBloc.loginEmailController.text,
password: value,
));
@ -348,17 +347,18 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
},
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
hintStyle: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
suffixIcon: IconButton(
onPressed: () {
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
loginBloc
.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
},
icon: SizedBox(
child: SvgPicture.asset(
loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword,
loginBloc.obscureText
? Assets.visiblePassword
: Assets.invisiblePassword,
height: 15,
width: 15,
),
@ -386,10 +386,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
},
child: Text(
"Forgot Password?",
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
),
),
],
@ -466,8 +464,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
style: Theme.of(context).textTheme.labelLarge!.copyWith(
fontSize: 14,
color: loginBloc.checkValidate
? ColorsManager.whiteColors
: ColorsManager.whiteColors.withOpacity(0.2),
? ColorsManager.white
: ColorsManager.white.withOpacity(0.2),
)),
onPressed: () {
if (loginBloc.loginFormKey.currentState!.validate()) {
@ -494,7 +492,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
SizedBox(
child: Text(
loginBloc.validate,
style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red),
style: const TextStyle(
fontWeight: FontWeight.w700, color: ColorsManager.red),
),
)
],

View File

@ -56,7 +56,7 @@ class SearchResetButtons extends StatelessWidget {
decoration: containerDecoration,
child: Center(
child: DefaultButton(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
borderRadius: 9,
onPressed: onReset,
child: Text(

View File

@ -35,7 +35,7 @@ class CurtainToggle extends StatelessWidget {
children: [
ClipOval(
child: Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: SvgPicture.asset(
Assets.curtainIcon,
width: 60,

View File

@ -127,13 +127,11 @@ class _DynamicTableState extends State<DynamicTable> {
controller: _horizontalScrollController,
thumbVisibility: true,
trackVisibility: true,
notificationPredicate: (notif) =>
notif.metrics.axis == Axis.horizontal,
notificationPredicate: (notif) => notif.metrics.axis == Axis.horizontal,
child: SingleChildScrollView(
controller: _horizontalScrollController,
scrollDirection: Axis.horizontal,
physics:
widget.isEmpty ? const NeverScrollableScrollPhysics() : null,
physics: widget.isEmpty ? const NeverScrollableScrollPhysics() : null,
child: SizedBox(
width: _totalTableWidth,
child: Column(
@ -195,29 +193,25 @@ class _DynamicTableState extends State<DynamicTable> {
widget.headers[colIndex] == 'Settings'
? buildSettingsIcon(
width: _settingsColumnWidth,
onTap: () => widget
.onSettingsPressed
onTap: () => widget.onSettingsPressed
?.call(rowIndex),
)
: _buildTableCell(
row[colIndex].toString(),
width: widget.headers[
colIndex] ==
width: widget.headers[colIndex] ==
'Settings'
? _settingsColumnWidth
: (_totalTableWidth -
(widget.withCheckBox
? _checkboxColumnWidth
: 0) -
(widget.headers
.contains(
'Settings')
(widget.headers.contains(
'Settings')
? _settingsColumnWidth
: 0)) /
(widget.headers.length -
(widget.headers
.contains(
'Settings')
(widget.headers.contains(
'Settings')
? 1
: 0)),
rowIndex: rowIndex,
@ -241,7 +235,7 @@ class _DynamicTableState extends State<DynamicTable> {
Widget _buildEmptyState() => Container(
height: widget.size.height,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -281,9 +275,8 @@ class _DynamicTableState extends State<DynamicTable> {
),
child: Checkbox(
value: _selectAll,
onChanged: widget.withSelectAll && widget.data.isNotEmpty
? _toggleSelectAll
: null,
onChanged:
widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
),
);
}
@ -300,7 +293,7 @@ class _DynamicTableState extends State<DynamicTable> {
width: 1.0,
),
),
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
alignment: Alignment.centerLeft,
child: Center(
@ -341,9 +334,7 @@ class _DynamicTableState extends State<DynamicTable> {
}
Widget _buildTableCell(String content,
{required double width,
required int rowIndex,
required int columnIndex}) {
{required double width, required int rowIndex, required int columnIndex}) {
bool isBatteryLevel = content.endsWith('%');
double? batteryLevel;
@ -415,7 +406,7 @@ class _DynamicTableState extends State<DynamicTable> {
height: _fixedRowHeight,
padding: const EdgeInsets.only(left: 15, top: 10, bottom: 10),
decoration: const BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: Border(
bottom: BorderSide(
color: ColorsManager.boxDivider,

View File

@ -27,8 +27,8 @@ class BatchAcMode extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildIconContainer(context, TempModes.cold, Assets.freezing,
value == TempModes.cold),
_buildIconContainer(
context, TempModes.cold, Assets.freezing, value == TempModes.cold),
_buildIconContainer(
context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner,
@ -56,7 +56,7 @@ class BatchAcMode extends StatelessWidget {
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,

View File

@ -32,8 +32,8 @@ class BatchFanSpeedControl extends StatelessWidget {
children: [
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto,
value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow,
value == FanSpeeds.low),
_buildIconContainer(
context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
],
),
const SizedBox(height: 8),
@ -52,8 +52,8 @@ class BatchFanSpeedControl extends StatelessWidget {
);
}
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
String assetPath, bool isSelected) {
Widget _buildIconContainer(
BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
return GestureDetector(
onTap: () {
context.read<AcBloc>().add(
@ -69,7 +69,7 @@ class BatchFanSpeedControl extends StatelessWidget {
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,

View File

@ -27,8 +27,8 @@ class AcMode extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildIconContainer(context, TempModes.cold, Assets.freezing,
value == TempModes.cold),
_buildIconContainer(
context, TempModes.cold, Assets.freezing, value == TempModes.cold),
_buildIconContainer(
context, TempModes.hot, Assets.acSun, value == TempModes.hot),
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner,
@ -56,7 +56,7 @@ class AcMode extends StatelessWidget {
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,

View File

@ -38,7 +38,7 @@ class AcToggle extends StatelessWidget {
height: 60,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
padding: const EdgeInsets.all(8),
child: ClipOval(

View File

@ -31,8 +31,8 @@ class FanSpeedControl extends StatelessWidget {
children: [
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto,
value == FanSpeeds.auto),
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow,
value == FanSpeeds.low),
_buildIconContainer(
context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
],
),
const SizedBox(height: 8),
@ -51,8 +51,8 @@ class FanSpeedControl extends StatelessWidget {
);
}
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
String assetPath, bool isSelected) {
Widget _buildIconContainer(
BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
return GestureDetector(
onTap: () {
context.read<AcBloc>().add(
@ -68,7 +68,7 @@ class FanSpeedControl extends StatelessWidget {
height: 50,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
border: Border.all(
color: isSelected ? Colors.blue : Colors.transparent,
width: 2.0,

View File

@ -35,8 +35,7 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) =>
DeviceManagementBloc()..add(FetchDevices(context)),
create: (context) => DeviceManagementBloc()..add(FetchDevices(context)),
),
],
child: WebScaffold(
@ -59,8 +58,9 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
BlocProvider.of<CreateRoutineBloc>(context)
.add(const ResetSelectedEvent());
context.read<RoutineBloc>().add(
const TriggerSwitchTabsEvent(isRoutineTab: false));
context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context
.read<DeviceManagementBloc>()
.add(FetchDevices(context));
@ -69,7 +69,7 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
'Devices',
style: context.textTheme.titleMedium?.copyWith(
color: !state.routineTab
? ColorsManager.whiteColors
? ColorsManager.white
: ColorsManager.grayColor,
fontWeight:
!state.routineTab ? FontWeight.w700 : FontWeight.w400,
@ -86,17 +86,17 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
BlocProvider.of<CreateRoutineBloc>(context)
.add(const ResetSelectedEvent());
context.read<RoutineBloc>().add(
const TriggerSwitchTabsEvent(isRoutineTab: true));
context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: true));
},
child: Text(
'Workflow Automation',
style: context.textTheme.titleMedium?.copyWith(
color: state.routineTab
? ColorsManager.whiteColors
? ColorsManager.white
: ColorsManager.grayColor,
fontWeight:
state.routineTab ? FontWeight.w700 : FontWeight.w400,
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400,
),
),
),
@ -120,8 +120,7 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
} else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) {
return DeviceManagementBody(
devices: deviceState.filteredDevices);
return DeviceManagementBody(devices: deviceState.filteredDevices);
} else {
return const DeviceManagementBody(devices: []);
}

View File

@ -120,8 +120,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
content: Text(
'This Device is Offline',
),
duration:
Duration(seconds: 2),
duration: Duration(seconds: 2),
),
);
return;
@ -135,13 +134,11 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
device: selectedDevices.first,
),
);
} else if (selectedDevices.length >
1) {
final productTypes =
selectedDevices
.map((device) =>
device.productType)
.toSet();
} else if (selectedDevices.length > 1) {
final productTypes = selectedDevices
.map(
(device) => device.productType)
.toSet();
if (productTypes.length == 1) {
showDialog(
context: context,
@ -224,13 +221,11 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
device.batteryLevel != null
? '${device.batteryLevel}%'
: '-',
formatDateTime(
DateTime.fromMillisecondsSinceEpoch(
(device.createTime ?? 0) * 1000)),
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.createTime ?? 0) * 1000)),
device.online == true ? 'Online' : 'Offline',
formatDateTime(
DateTime.fromMillisecondsSinceEpoch(
(device.updateTime ?? 0) * 1000)),
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.updateTime ?? 0) * 1000)),
'Settings',
];
}).toList(),
@ -273,7 +268,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: Material(
child: Container(
width: MediaQuery.of(context).size.width * 0.3,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: DeviceSettingsPanel(
device: device,
onClose: () => Navigator.of(context).pop(),

View File

@ -18,7 +18,7 @@ class AccurteCalibratingDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Calibrating',

View File

@ -16,7 +16,7 @@ class AccurateCalibrationDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Accurate Calibration',

View File

@ -17,7 +17,7 @@ class CalibrateCompletedDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
content: SizedBox(
height: 250,

View File

@ -20,7 +20,7 @@ class CurtainActionWidget extends StatelessWidget {
height: 60,
width: 60,
padding: const EdgeInsets.all(8),
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: ClipOval(
child: Container(
height: 60,

View File

@ -164,7 +164,7 @@ class _CurtainSliderWidgetState extends State<CurtainSliderWidget> {
divisions: 10, // 10% step
activeColor: ColorsManager.minBlueDot,
thumbColor: ColorsManager.primaryColor,
inactiveColor: ColorsManager.whiteColors,
inactiveColor: ColorsManager.white,
// Start dragging — use local control
onChangeStart: (_) {

View File

@ -47,10 +47,9 @@ class PrefReversCardWidget extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
borderRadius: const BorderRadius.horizontal(
left: Radius.circular(10),
right: Radius.circular(10)),
left: Radius.circular(10), right: Radius.circular(10)),
border: Border.all(color: ColorsManager.grayBorder)),
child: SvgPicture.asset(
Assets.reverseArrows,

View File

@ -22,7 +22,7 @@ class CurtainModulePrefrencesDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.CircleImageBackground,
backgroundColor: ColorsManager.circleImageBackground,
contentPadding: const EdgeInsets.all(20),
title: Center(
child: Text(

View File

@ -63,7 +63,7 @@ class _QuickCalibratingDialogState extends State<QuickCalibratingDialog> {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Calibrating',

View File

@ -18,7 +18,7 @@ class QuickCalibrationDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
contentPadding: EdgeInsets.zero,
content: AccurateDialogWidget(
title: 'Quick Calibration',

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
@ -47,8 +46,8 @@ class DeviceSettingsPanel extends StatelessWidget {
return BlocBuilder<SettingDeviceBloc, DeviceSettingsState>(
builder: (context, state) {
final _bloc = context.read<SettingDeviceBloc>();
final iconPath = DeviceIconTypeHelper.getDeviceIconByTypeCode(
device.productType);
final iconPath =
DeviceIconTypeHelper.getDeviceIconByTypeCode(device.productType);
final deviceInfo = state is DeviceSettingsUpdate
? state.deviceInfo ?? DeviceInfoModel.empty()
: DeviceInfoModel.empty();
@ -77,11 +76,9 @@ class DeviceSettingsPanel extends StatelessWidget {
children: [
Text(
'Device Settings',
style: context.theme.textTheme.titleLarge!
.copyWith(
style: context.theme.textTheme.titleLarge!.copyWith(
fontWeight: FontWeight.w700,
color: ColorsManager.vividBlue
.withOpacity(0.7),
color: ColorsManager.vividBlue.withOpacity(0.7),
fontSize: 24),
),
],
@ -98,7 +95,7 @@ class DeviceSettingsPanel extends StatelessWidget {
backgroundColor:
ColorsManager.grayBorder.withOpacity(0.5),
child: CircleAvatar(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
radius: 36,
child: SvgPicture.asset(
iconPath,
@ -116,8 +113,7 @@ class DeviceSettingsPanel extends StatelessWidget {
const SizedBox(height: 15),
Text(
'Device Name:',
style: context.textTheme.bodyMedium!
.copyWith(
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
),
),
@ -143,13 +139,13 @@ class DeviceSettingsPanel extends StatelessWidget {
_bloc.add(const ChangeNameEvent(
value: false));
deviceManagementBloc
..add(UpdateDeviceName(
deviceId: device.uuid!,
newName: _bloc
.nameController
.text))..add(ResetSelectedDevices());
..add(UpdateDeviceName(
deviceId: device.uuid!,
newName:
_bloc.nameController.text))
..add(ResetSelectedDevices());
},
decoration:const InputDecoration(
decoration: const InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
@ -164,18 +160,16 @@ class DeviceSettingsPanel extends StatelessWidget {
width: 15,
height: 25,
child: Visibility(
visible:
_bloc.editName != true,
visible: _bloc.editName != true,
replacement: const SizedBox(),
child: InkWell(
onTap: () {
_bloc.add(
const ChangeNameEvent(
value: true));
value: true));
},
child: SvgPicture.asset(
Assets
.editNameIconSettings,
Assets.editNameIconSettings,
color: ColorsManager
.lightGrayBorderColor,
height: 15,

View File

@ -32,7 +32,7 @@ class _SubSpaceDialogState extends State<SubSpaceDialog> {
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: ColorsManager.whiteColors,
backgroundColor: ColorsManager.white,
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 60),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(28),

View File

@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/schedule_view/schedule_garage_view.dart';
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedual_view.dart';
import 'package:syncrow_web/pages/device_managment/shared/icon_name_status_container.dart';
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
@ -94,11 +95,18 @@ class GarageDoorControlView extends StatelessWidget
FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'doorcontact_state'),
);
showDialog(
showDialog<void>(
context: context,
builder: (ctx) => BlocProvider.value(
value: BlocProvider.of<GarageDoorBloc>(context),
child: BuildGarageDoorScheduleView(status: status),
child: BuildScheduleView(
deviceUuid: deviceId,
category: 'Timer',
code: 'doorcontact_state',
countdownCode: 'Timer',
deviceType: 'GD',
),
));
},
name: 'Scheduling',

View File

@ -109,7 +109,7 @@ class _DeviceItem extends StatelessWidget {
height: 60,
width: 60,
padding: const EdgeInsets.all(8),
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: SvgPicture.asset(
device.icon ?? 'assets/icons/gateway.svg',
width: 35,

View File

@ -36,7 +36,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
@override
Widget build(BuildContext context) {
return Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: Column(
children: [
const Row(

View File

@ -22,7 +22,7 @@ class PowerClampInfoCard extends StatelessWidget {
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
borderRadius: BorderRadius.circular(20),
),
height: 55,

View File

@ -12,8 +12,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
//Smart Power Clamp
class SmartPowerDeviceControl extends StatelessWidget
with HelperResponsiveLayout {
class SmartPowerDeviceControl extends StatelessWidget with HelperResponsiveLayout {
final String deviceId;
const SmartPowerDeviceControl({super.key, required this.deviceId});
@ -129,7 +128,7 @@ class SmartPowerDeviceControl extends StatelessWidget
bottom: 10,
),
decoration: BoxDecoration(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
borderRadius: BorderRadius.circular(20),
),
height: 300,
@ -149,8 +148,7 @@ class SmartPowerDeviceControl extends StatelessWidget
onPressed: blocProvider.currentPage <= 0
? null
: () {
blocProvider
.add(SmartPowerArrowPressedEvent(-1));
blocProvider.add(SmartPowerArrowPressedEvent(-1));
pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
@ -172,8 +170,7 @@ class SmartPowerDeviceControl extends StatelessWidget
onPressed: blocProvider.currentPage >= 3
? null
: () {
blocProvider
.add(SmartPowerArrowPressedEvent(1));
blocProvider.add(SmartPowerArrowPressedEvent(1));
pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
@ -202,8 +199,8 @@ class SmartPowerDeviceControl extends StatelessWidget
blocProvider.add(SelectDateEvent(context: context));
blocProvider.add(FilterRecordsByDateEvent(
selectedDate: blocProvider.dateTime!,
viewType: blocProvider
.views[blocProvider.currentIndex]));
viewType:
blocProvider.views[blocProvider.currentIndex]));
},
widget: blocProvider.dateSwitcher(),
chartData: blocProvider.energyDataList.isNotEmpty

View File

@ -287,7 +287,8 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
try {
if (state is ScheduleLoaded) {
Status status = Status(code: '', value: '');
if (event.deviceType == 'CUR_2') {
if (event.deviceType == 'CUR_2' ||
event.deviceType == 'GD' ) {
status = status.copyWith(
code: 'control',
value: event.functionOn == true ? 'open' : 'close');

View File

@ -69,7 +69,7 @@ class CountdownModeButtons extends StatelessWidget {
countDownCode: countDownCode),
);
},
backgroundColor: ColorsManager.primaryColorWithOpacity,
backgroundColor: ColorsManager.secondaryColor,
child: const Text('Save'),
),
),

View File

@ -63,7 +63,7 @@ class InchingModeButtons extends StatelessWidget {
),
);
},
backgroundColor: ColorsManager.primaryColor,
backgroundColor: ColorsManager.secondaryColor,
child: const Text('Save'),
),
),

View File

@ -31,11 +31,12 @@ class BuildScheduleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ScheduleBloc(deviceId: deviceUuid,)
create: (_) => ScheduleBloc(
deviceId: deviceUuid,
)
..add(ScheduleGetEvent(category: category))
..add(ScheduleFetchStatusEvent(
deviceId: deviceUuid,
countdownCode: countdownCode ?? '')),
deviceId: deviceUuid, countdownCode: countdownCode ?? '')),
child: Dialog(
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20),
@ -56,7 +57,7 @@ class BuildScheduleView extends StatelessWidget {
children: [
const ScheduleHeader(),
const SizedBox(height: 20),
if (deviceType == 'CUR_2')
if (deviceType == 'CUR_2' || deviceType == 'GD')
const SizedBox()
else
ScheduleModeSelector(
@ -76,8 +77,7 @@ class BuildScheduleView extends StatelessWidget {
category: category,
time: '',
function: Status(
code: code.toString(),
value: true),
code: code.toString(), value: true),
days: [],
),
isEdit: false,
@ -96,7 +96,7 @@ class BuildScheduleView extends StatelessWidget {
}
},
),
if (deviceType != 'CUR_2')
if (deviceType != 'CUR_2'|| deviceType != 'GD')
if (state.scheduleMode == ScheduleModes.countdown ||
state.scheduleMode == ScheduleModes.inching)
CountdownInchingView(

View File

@ -30,7 +30,7 @@ class ScheduleControlButton extends StatelessWidget {
height: 60,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.whiteColors,
color: ColorsManager.white,
),
margin: const EdgeInsets.symmetric(horizontal: 4),
padding: const EdgeInsets.all(12),

View File

@ -13,7 +13,7 @@ class ScheduleHeader extends StatelessWidget {
Text(
'Scheduling',
style: TextStyle(
color: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.opaquePrimary,
fontWeight: FontWeight.w700,
fontSize: 30,
),

View File

@ -24,12 +24,13 @@ class ScheduleManagementUI extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 170,
width: 177,
height: 40,
child: DefaultButton(
borderColor: ColorsManager.grayColor.withOpacity(0.5),
padding: 2,
backgroundColor: ColorsManager.graysColor,
borderWidth: 4,
borderColor: ColorsManager.neutralGray,
padding: 8,
backgroundColor: ColorsManager.textFieldGreyColor,
borderRadius: 15,
onPressed: onAddSchedule,
child: Row(

View File

@ -39,7 +39,7 @@ class ScheduleModeButtons extends StatelessWidget {
borderRadius: 8,
height: 40,
onPressed: onSave,
backgroundColor: ColorsManager.primaryColorWithOpacity,
backgroundColor: ColorsManager.secondaryColor,
child: const Text('Save'),
),
),

View File

@ -194,7 +194,7 @@ class _ScheduleTableView extends StatelessWidget {
child: Text(_getSelectedDays(
ScheduleModel.parseSelectedDays(schedule.days)))),
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
if (deviceType == 'CUR_2')
if (deviceType == 'CUR_2' || deviceType == 'GD')
Center(
child: Text(schedule.function.value == true ? 'open' : 'close'))
else

View File

@ -76,7 +76,7 @@ class _FactoryResetWidgetState extends State<FactoryResetWidget> {
child: Text(
'Reset',
style: context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
fontWeight: FontWeight.w400,
fontSize: 12,
),
@ -95,7 +95,7 @@ class _FactoryResetWidgetState extends State<FactoryResetWidget> {
children: [
ClipOval(
child: Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
height: 60,
width: 60,
child: Padding(

View File

@ -45,7 +45,7 @@ class IconNameStatusContainer extends StatelessWidget {
height: 60,
width: 60,
padding: EdgeInsets.all(paddingAmount ?? 8),
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: SvgPicture.asset(
icon,
width: 35,

View File

@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class PresenceSpaceType extends StatelessWidget {
const PresenceSpaceType({
@ -59,7 +59,7 @@ class PresenceSpaceType extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
color: value == spaceType
? ColorsManager.primaryColorWithOpacity
? ColorsManager.opaquePrimary
: ColorsManager.textGray,
),
child: SvgPicture.asset(

View File

@ -52,7 +52,7 @@ class ToggleWidget extends StatelessWidget {
height: 60,
width: 60,
padding: const EdgeInsets.all(8),
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: SvgPicture.asset(
icon ?? Assets.lightPulp,
width: 35,

View File

@ -1,5 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
@ -7,7 +6,8 @@ import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class CeilingLight extends StatelessWidget {
const CeilingLight({super.key, required this.value, required this.code, required this.deviceId});
const CeilingLight(
{super.key, required this.value, required this.code, required this.deviceId});
final bool value;
final String code;
@ -24,7 +24,7 @@ class CeilingLight extends StatelessWidget {
children: [
ClipOval(
child: Container(
color: ColorsManager.whiteColors,
color: ColorsManager.white,
child: SvgPicture.asset(
Assets.lightPulp,
width: 60,

Some files were not shown because too many files have changed in this diff Show More