Compare commits

..

232 Commits

Author SHA1 Message Date
acefe6b433 fixed repeated duplication 2025-04-29 10:13:11 +04:00
63bc7a56de - Refactor the code in _RoutinesViewState to improve readability and maintainability.
- Update the indentation and add padding to the child widgets in the Column.
- Add a bottom padding to the empty state text in _buildEmptyState.
2025-04-28 16:49:22 +03:00
7b3635deae Merge pull request #166 from SyncrowIOT/bugfix/clear-search
fixed community search caching
2025-04-28 16:27:38 +04:00
58755eafe1 Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/clear-search 2025-04-28 16:26:47 +04:00
ce225818fb fixed community search caching 2025-04-28 16:25:43 +04:00
8762a7aaa8 Merge pull request #165 from SyncrowIOT/bugfix/white-page-rendering 2025-04-28 15:22:37 +04:00
8dc833b2c3 fixed blank page rendering 2025-04-28 15:21:28 +04:00
13cef151aa Merge pull request #164 from SyncrowIOT/bugfix/space-model-with-tags 2025-04-28 14:37:26 +04:00
ab23be9828 fixed the issue in selecting space model and tag 2025-04-28 14:36:27 +04:00
687b68ab22 Merge pull request #163 from SyncrowIOT/fix/duplication-flatten
Fix/duplication-flatten
2025-04-28 13:00:17 +04:00
25614c3dd0 fix the flatten 2025-04-28 12:59:16 +04:00
7cbe20ae88 remove unused code 2025-04-28 12:56:29 +04:00
349fe6c555 realignment 2025-04-28 11:58:41 +04:00
9779f3783c Merge branch 'dev' of https://github.com/SyncrowIOT/web into dev 2025-04-28 10:03:31 +04:00
fe3db663b6 realign initially 2025-04-28 10:03:27 +04:00
888d444752 Merge pull request #160 from SyncrowIOT/SP-1478-FE-On-devices-management-page-when-we-open-power-clamp-device-loading-indicator-remains-loading-and-no-data-is-displayed
Sp 1478 fe on devices management page when we open power clamp device loading indicator remains loading and no data is displayed
2025-04-28 08:59:17 +03:00
bab5e06968 Merge pull request #159 from SyncrowIOT/SP-1463-rework
Sp 1463 rework
2025-04-28 08:58:39 +03:00
d7b6174dee Merge pull request #162 from SyncrowIOT:bugfix/save-spaces
fixed the save issue
2025-04-28 00:37:33 +04:00
6ef0b2f9d1 fixed the save issue 2025-04-28 00:36:58 +04:00
3ceb03826e Merge pull request #161 from SyncrowIOT/bugfix/searchquery 2025-04-27 22:44:57 +04:00
52608b1f35 fixed search query 2025-04-27 22:42:57 +04:00
ac2996629e resolved an exception that was thrown when resizing the DeviceSearchFilters. 2025-04-27 15:42:50 +03:00
51c52c66cb SP-1478-FE-On-devices-management-page-when-we-open-power-clamp-device-loading-indicator-remains-loading-and-no-data-is-displayed 2025-04-27 15:18:19 +03:00
0f56273d99 SP-1408 2025-04-27 12:10:38 +03:00
34d4d892d9 refactor: streamline value calculations in FlushMountedPresenceSensorControlView 2025-04-27 11:11:38 +03:00
3193fd26fe refactor: update presence delay value and enhance request handling in DebouncedBatchControlDevicesService 2025-04-27 11:04:54 +03:00
43802a9fad refactor: update detection value calculations and adjust parameters for presence delay 2025-04-27 10:57:44 +03:00
6e0b1775f0 fix: reorder constructor parameters for consistency in FlushMountedPresenceSensorControlView 2025-04-27 10:55:18 +03:00
233fb2ee2c refactor: improve formatting of clamp method for near and far detection values 2025-04-27 10:55:03 +03:00
b26928b3d5 fixed ui bugs. 2025-04-27 10:14:35 +03:00
6fc35a7b9a enhanced device debouncing to accomodate for multiple calls from the different devices functions calls. 2025-04-27 10:14:19 +03:00
756457927c removed unnecessary isBatch flag from FlushMountedPresenceSensorChangeValueEvent. 2025-04-27 10:13:53 +03:00
f30d7c0117 Merge pull request #158 from SyncrowIOT:bugfix/duplicate-space
Bugfix/duplicate-space
2025-04-27 11:13:07 +04:00
976d6e385a duplicated space 2025-04-27 11:12:03 +04:00
ff07e7509d fixed the issue in aligning child space 2025-04-26 16:04:41 +04:00
17a582ab99 Merge pull request #157 from SyncrowIOT/SP-1415 2025-04-25 10:50:06 +04:00
09fb604acc added filtering 2025-04-25 10:49:25 +04:00
2068df173d Merge pull request #155 from SyncrowIOT/SP-1441-rework-FE-On-routine-creation-page-When-the-user-drags-a-card-that-has-signs-and-selects-a-sign-without-a-number-then-confirms-the-value-appears-to-be-Null
Sp 1441 rework fe on routine creation page when the user drags a card that has signs and selects a sign without a number then confirms the value appears to be null
2025-04-24 16:25:39 +03:00
bfc2a381d2 Merge pull request #156 from SyncrowIOT/SP-1464-FE-implement-Batch-Control-Dialog
Sp 1464 fe implement batch control dialog
2025-04-24 16:25:17 +03:00
778257644d reduced debounce duration. 2025-04-24 15:13:10 +03:00
c8e540e938 Remove unnecessary event dispatch in FlushMountedPresenceSensorBlocFactory creation 2025-04-24 14:29:18 +03:00
ba20998067 disabled realtime on batch control. 2025-04-24 14:21:38 +03:00
75b0b24543 Add Flush Mounted Presence Sensor support and update event handling 2025-04-24 14:13:13 +03:00
c03b8f290c refactor function tap handlers to use RoutineTapFunctionHelper for improved code reuse and readability, and to remove code duplication. 2025-04-24 10:25:41 +03:00
2c684a9495 SP-1441 rework. 2025-04-23 16:58:50 +03:00
fbc45b465f Merge pull request #153 from SyncrowIOT/SP-1344-FE-Real-Time-Issues-Ceiling-Sensor-AC-and-Garage-Door-Sensor
Refactor widget lifecycle methods for temperature control and presenc…
2025-04-23 16:29:36 +03:00
c9c939c59f Merge pull request #154 from SyncrowIOT/Add-Flush-Mounted-Presence-Sensor-Single-Control
Add flush mounted presence sensor single control
2025-04-23 16:21:17 +03:00
e1a2465130 Fix deviceId assignment in FlushMountedPresenceSensorBlocFactory and update _listenToChanges method for async handling 2025-04-23 13:11:25 +03:00
86164e746a Refactor FlushMountedPresenceSensorBloc creation to use factory method and streamline dependency injection 2025-04-23 12:15:50 +03:00
4a5176cf22 Refactor presence update data handling for improved precision and scaling 2025-04-23 12:11:13 +03:00
2bb7a6950a Refactor debounce duration handling in BatchControlDevicesService and ControlDeviceService 2025-04-23 10:52:08 +03:00
46860619a0 Add bloc dependency to pubspec.yaml 2025-04-23 10:48:24 +03:00
d1bb7b129f Refactor widget lifecycle methods for temperature control and presence sensor 2025-04-23 10:46:56 +03:00
7adce3b94c Refactor _onFlushMountedPresenceSensorFetchBatchStatusEvent to use final for response variable 2025-04-23 10:44:44 +03:00
91f93d4395 Refactor FlushMountedPresenceSensorBloc to streamline device control logic and remove redundant code 2025-04-23 10:44:11 +03:00
42d6b64e58 Refactor FlushMountedPresenceSensorBloc to replace _runDeBouncer with _controlDevice for handling device control logic 2025-04-23 10:32:49 +03:00
b11d4186fb Add BatchControlDevicesService integration to FlushMountedPresenceSensorBloc and ControlView 2025-04-23 10:31:26 +03:00
24130be665 organized instances in bloc. 2025-04-23 10:26:55 +03:00
8c960bd5f1 created BatchControlDevicesService interface. 2025-04-23 10:26:17 +03:00
fb8ccdf0a6 Add FlushMountedPresenceSensorControlView for managing presence sensor settings 2025-04-23 10:24:38 +03:00
1975a1b6f4 created FlushMountedPresenceSensorBloc, events, and states for device management. 2025-04-23 10:24:14 +03:00
367d6717e7 Refactor PresenceUpdateData widget to support decimal values. 2025-04-23 10:23:28 +03:00
8c637e40ff Created FlushMountedPresenceSensorModel model. 2025-04-23 10:23:00 +03:00
05b3180510 Created ControlDeviceService interface and its remote and debounced implementation. 2025-04-23 10:22:41 +03:00
177a4711fe Synced main_dev and main_staging with main.dart 2025-04-22 03:39:27 +03:00
d7a37c6519 Removed flavor from Flutter run and build commands 2025-04-22 03:01:43 +03:00
b901791079 Update Flutter run and build commands, and added main_staging file 2025-04-22 02:33:50 +03:00
5d16555748 Merge pull request #151 from SyncrowIOT/SP-1447-FE-Invalid-Time-Limit-24h-Exceeding-Backend-Max-of-12h
SP-1447.
2025-04-21 18:59:57 +03:00
bd15f30b8a Merge pull request #150 from SyncrowIOT/SP-1440-FE-On-routine-creation-edit-Page-When-saving-a-routine-the-confirmation-pop-up-is-not-identical-to-design
Sp 1440 fe on routine creation edit page when saving a routine the confirmation pop up is not identical to design
2025-04-21 18:59:37 +03:00
5f2684bdf4 Merge pull request #152 from SyncrowIOT/SP-702-WEB-Username-Caching-After-Logout-Requires-Screen-Refresh
Sp 702 web username caching after logout requires screen refresh
2025-04-21 18:58:57 +03:00
b4ef22ef0a SP-702/ clears user data on logging out. 2025-04-21 14:23:40 +03:00
d8afb562eb SP-1447, set the limit of gangs countdown to be 12 hours instead of 24 hours. 2025-04-21 12:07:21 +03:00
67a164e6d2 modified divider color to match figma design. 2025-04-21 10:59:09 +03:00
cf20bdcd42 SP-1440 2025-04-21 10:57:46 +03:00
32b45ea5d7 Merge pull request #149 from SyncrowIOT/SP-1441-FE-On-routine-creation-page-When-the-user-drags-a-card-that-has-signs-and-selects-a-sign-without-a-number-then-confirms-the-value-appears-to-be-Null
Sp 1441 fe on routine creation page when the user drags a card that has signs and selects a sign without a number then confirms the value appears to be null
2025-04-21 10:32:38 +03:00
62fb8b3097 SP-1441 2025-04-21 10:28:44 +03:00
065bd33511 SP-1330. 2025-04-21 10:03:02 +03:00
b9ab782c01 Merge pull request #148 from SyncrowIOT/SP-1435-FE-On-routines-page-when-the-screen-height-is-decreased-or-is-small-the-scroll-area-for-routine-cards-is-not-fitting-the-whole-screen-width
Sp 1435 fe on routines page when the screen height is decreased or is small the scroll area for routine cards is not fitting the whole screen width
2025-04-21 09:49:08 +03:00
4d5adf948c Merge pull request #147 from SyncrowIOT/SP-1433-FE-Text-Alignment-Issue-in-UI-Component-in-adding-subspace-in-a-space
Sp 1433 fe text alignment issue in UI component in adding subspace in a space
2025-04-21 09:48:38 +03:00
e45f57ca03 SP-1441 2025-04-17 16:55:44 +03:00
84264391d9 progress towards matching the design of save routine dialog. 2025-04-17 16:14:26 +03:00
2e4f904d3a style: improve code formatting and readability in SaveRoutineHelper 2025-04-17 14:39:33 +03:00
c46cfb48a8 refactor: extract IF and THEN sections into separate methods for better readability 2025-04-17 14:38:04 +03:00
a0dd128557 matched design of Function and action in SaveRoutineHelper.showSaveRoutineDialog 2025-04-17 14:30:30 +03:00
34fa426163 submitting password field in login logs the user in. 2025-04-17 14:17:43 +03:00
1407c173b0 tapping bugfix. 2025-04-17 13:28:00 +03:00
9431eb79c1 Fix loading state handling and refactor scene/automation rendering in FetchRoutineScenesAutomation 2025-04-17 13:18:44 +03:00
70f1f39fce Improve loading state handling and in FetchRoutineScenesAutomation. 2025-04-17 13:13:48 +03:00
f912b41fd8 Refactor visibility handling for scenes and automations in FetchRoutineScenesAutomation 2025-04-17 13:12:09 +03:00
977875f1f2 SP-1435 2025-04-17 13:08:16 +03:00
8136804694 bugfix. 2025-04-17 12:18:09 +03:00
ce253b2034 Remove unused parameters from CreateSubSpaceDialog constructor 2025-04-17 11:27:37 +03:00
024fbcdb83 Refactor CreateSubSpaceDialog to convert to StatefulWidget and manage text controller lifecycle 2025-04-17 11:25:39 +03:00
a8430a7d3d Merge pull request #145 from SyncrowIOT/SP-1330-FE-Side-tree-text-breaks-incorrectly-causing-layout-issues
Sp 1330 fe side tree text breaks incorrectly causing layout issues
2025-04-17 09:49:33 +03:00
7ef6020dd8 Merge pull request #144 from SyncrowIOT/SP-1333-FE-set-barrier-dismissable-to-true
Sp 1333 fe set barrier dismissable to true
2025-04-17 09:49:11 +03:00
2a77483f46 Refactor CreateSubSpaceDialog to improve widget structure and readability 2025-04-17 09:45:14 +03:00
a6fc99443b Refactor CreateSubSpaceDialog layout for improved readability and maintainability 2025-04-17 09:38:54 +03:00
ae95d06482 Fix constructor parameter order in CreateSubSpaceDialog 2025-04-17 09:35:47 +03:00
18c886753d added trailing commas wherever neccessary in CreateSubSpaceDialog. 2025-04-17 09:35:33 +03:00
62bf4f2944 Refactor CreateSubSpaceDialog to use context extension for screen width calculations 2025-04-17 09:34:03 +03:00
726c173a76 SP-1433-FE-Text-Alignment-Issue-in-UI-Component-in-adding-subspace-in-a-space 2025-04-17 09:33:10 +03:00
d538b3667e Merge pull request #146 from SyncrowIOT/Fix-Factory-Reset-Model
Refactor FactoryResetModel and MainDoorSensorBatchView
2025-04-17 09:18:43 +03:00
72ae3b1727 Refactor FactoryResetModel and MainDoorSensorBatchView
- Refactor FactoryResetModel to include 'operationType' in toJson and toMap methods.
- Refactor MainDoorSensorBatchView to use BlocProvider and Builder for better state management.
2025-04-16 15:06:50 +03:00
01d5cb48cc Refactor onChanged callback in Checkbox for improved readability in CustomExpansionTileSpaceTree. 2025-04-16 14:49:20 +03:00
3216d6b879 Refactor fillColor assignment in CustomExpansionTileSpaceTree for improved readability. 2025-04-16 14:47:52 +03:00
52e1ff94de Refactor onItemSelected handling in CustomExpansionTileSpaceTree for improved readability. 2025-04-16 14:47:42 +03:00
0cc867a4ea Refactor text color assignment in CustomExpansionTileSpaceTree for improved readability. 2025-04-16 14:47:19 +03:00
3de7606a00 Refactor expansion icon handling in CustomExpansionTileSpaceTree for improved readability and maintainability. 2025-04-16 14:46:37 +03:00
f709b92e12 Refactor constructor formatting and improve readability in CustomExpansionTileSpaceTree. 2025-04-16 14:44:31 +03:00
f1667d4458 Refactor type annotations for onExpansionChanged and onItemSelected in CustomExpansionTileSpaceTree for improved clarity. 2025-04-16 14:44:11 +03:00
b4f03ab6c3 Initialize ScrollController in initState for better state management in SpaceTreeView. 2025-04-16 14:36:37 +03:00
4c38c50649 Refactor notification handling in SidebarCommunitiesList for improved readability and maintainability. 2025-04-16 14:35:29 +03:00
8b441aaf46 Refactor SidebarCommunitiesList to be a StatelessWidget and update its usage across SpaceTreeView and SidebarWidget for improved performance and maintainability. 2025-04-16 14:09:36 +03:00
afdd44e098 removed comments from SpaceTreeView. 2025-04-16 13:18:52 +03:00
fc1d394509 Extracted SidebarCommunitiesList into a reusable widget. 2025-04-16 13:17:09 +03:00
dce44e20ec Extracted EmptyResultsWidget into its own widget and file for reusability. 2025-04-16 13:11:56 +03:00
91c4c772b5 SP-1330. 2025-04-16 13:08:38 +03:00
e0be44a507 Merged with dev 2025-04-16 04:00:07 +03:00
d4a7dd5854 Fixed design issues, added tag and location to the save dialog 2025-04-16 03:46:10 +03:00
50eb890d18 Merge pull request #142 from SyncrowIOT/SP-1278-FE-Allow-Simple-Edit-Delete
Added Ceiling Presence Sensor Device To Routine
2025-04-15 16:56:05 +03:00
9eefd522b7 bump flutter version on production github action. 2025-04-15 16:25:00 +03:00
4989a0e95c removed the use of Flexible that was causing an exception. 2025-04-15 16:24:46 +03:00
3c6b9f9ef4 Merge branch 'dev' of https://github.com/SyncrowIOT/web into dev 2025-04-15 16:12:03 +03:00
86b8771694 bump-v of web deployment action. 2025-04-15 16:12:01 +03:00
ea1d3d18c8 Merge pull request #143 from SyncrowIOT/SP-1189-Rework-Add-Button-Not-clickable-Opening-Pop-up-in-Community-Screen
Sp 1189 rework add button not clickable opening pop up in community screen
2025-04-15 15:55:36 +03:00
9044645f95 remove screenWidth parameter from TagChipDisplay and use context.screenWidth instead. 2025-04-15 15:45:47 +03:00
7699453e6d moved styling of _buildChip up, and removed unnecessary SizedBox. 2025-04-15 15:43:50 +03:00
d1a21be983 removed else from TagChipDisplay.build 2025-04-15 15:35:24 +03:00
db8e5a4aa6 Refactor TagChipDisplay._groupedTags to enhance readabaility. 2025-04-15 15:32:31 +03:00
fa5bb350c3 refactor: replace spaceNameController with spaceName in TagChipDisplay. 2025-04-15 15:29:25 +03:00
920827d763 Removed unnecessary SizedBox from TagChipDisplay. 2025-04-15 15:28:12 +03:00
d3902d622e Moved constructor to be the first element in TagChipDisplay. 2025-04-15 15:27:45 +03:00
a4432656ab refactor: extract EditChip into a private method for improved readability 2025-04-15 15:27:04 +03:00
90e0d2f52b Extracted Chip into a private method. 2025-04-15 15:24:36 +03:00
08e5e17910 Extracted Add Devices button into a private method. 2025-04-15 15:23:36 +03:00
f57348e5cd converted to using expressions wherever possible in TagChipDisplay. 2025-04-15 15:13:47 +03:00
be168aed93 refactor: simplify tag checking logic to enhance readability. 2025-04-15 15:08:30 +03:00
a66784473f Replaced conditional with an if statement in TagChipDisplay. 2025-04-15 15:06:01 +03:00
c0a963ded5 refactor: use context extension for text theme. 2025-04-15 15:05:31 +03:00
7945cefe53 added trailing commas wherever necessary. 2025-04-15 15:05:06 +03:00
7d0e50fb1d removed unnecessary comments. 2025-04-15 15:03:27 +03:00
117f6190dd removed unnecessary BuildContext from TagChipDisplay constructor, sorted its properies, and converted to using super.key. 2025-04-15 15:02:58 +03:00
748c67fd8b SP-1333 2025-04-15 14:52:20 +03:00
1bfab8cc76 SP-1189-Fix tapping ok and nothing happening bug by taking the action out of the widget. 2025-04-15 14:38:06 +03:00
7dcaa20da1 Enhanced the code and look of DialogFooter buttons. 2025-04-15 13:06:30 +03:00
616adccfdd Applied the correct scenario of tapping add community icon button. 2025-04-15 12:58:20 +03:00
abf6555485 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1278-FE-Allow-Simple-Edit-Delete 2025-04-15 12:03:25 +03:00
be0533645e Refactor dialog header text assignment in CeilingSensorDialog for clarity and readability. 2025-04-15 11:20:15 +03:00
254e03e3c7 Add mapping for 'sports_para' in slider range function 2025-04-15 11:19:58 +03:00
db1f29e2b2 Merged with dev 2025-04-15 11:18:43 +03:00
dba89027e3 Updated the if statement for the tag and the location 2025-04-15 11:09:48 +03:00
6bea4c2f4a Add mappings for moving_range and presence_range in slider helpers 2025-04-15 10:34:05 +03:00
e2ec986bb9 Refactor mappable stepped functions to a static constant for improved readability and maintainability 2025-04-15 10:03:47 +03:00
ceb1e1d23a Remove TODO comments. 2025-04-15 10:03:37 +03:00
ee12980b47 Fix value parsing in CpsDialogSliderSelector to ensure two decimal precision 2025-04-15 10:03:21 +03:00
4849bb41ba Added function to single card, included tag and location to cards 2025-04-15 02:13:00 +03:00
ebcd89d2a5 Refactor ceiling sensor functions and update slider helper mappings for improved value handling 2025-04-14 16:24:50 +03:00
a7bdbfe3ec Merge pull request #141 from SyncrowIOT/fix-timer-toggle-issue
Refactor AC device controls and toggle widget
2025-04-14 16:06:37 +03:00
db84a9aa5e fix a logic 2025-04-14 16:03:34 +03:00
1493e35f6a removed unused method. 2025-04-14 14:35:24 +03:00
f19cc616be moved CpsSliderHelpers to its own file. 2025-04-14 14:32:49 +03:00
06383018b9 refactored helpers into a helper class to release some complexity out of the widget. 2025-04-14 14:32:22 +03:00
9e3a78f6b7 Update dialog header to reflect sensor condition type dynamically 2025-04-14 12:10:42 +03:00
a27b2e758c Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1278-FE-Allow-Simple-Edit-Delete 2025-04-14 12:06:42 +03:00
1023170788 Sort communities in create new routine dropdown. 2025-04-14 11:25:36 +03:00
9a2687d4c5 added some comments for clarity about how we should send the data to BE. 2025-04-14 11:13:27 +03:00
140f4ff5e2 Refactor AC device controls and toggle widget 2025-04-14 09:57:25 +03:00
cbaeecc968 Merge pull request #139 from SyncrowIOT/SP-1189-FE-Add-Button-Not-clickable-Opening-Pop-up-in-Community-Screen
Sp 1189 fe add button not clickable opening pop up in community screen
2025-04-13 16:22:37 +03:00
2a95720cb0 Merge pull request #140 from SyncrowIOT/SP-1345-FE-SOS-Button-UI-Issue
Refactor SosDeviceControlsView
2025-04-13 16:20:55 +03:00
4fae2d6be0 Refactor SosDeviceControlsView 2025-04-13 16:19:16 +03:00
5b84076572 Merge pull request #138 from SyncrowIOT/SP-1192-FE-Implement-the-schedule-on-the-web-for-the-AC-device
Sp 1192 fe implement the schedule on the web for the ac device
2025-04-13 15:02:02 +03:00
9bf37243a6 remove unused code and make a limitation for the nobody time picker 2025-04-13 14:55:55 +03:00
acad0e8c9c SP-1189-FE-Add-Button-Not-clickable-Opening-Pop-up-in-Community-Screen 2025-04-13 14:50:07 +03:00
79f5ef7871 simplify space selection logic. 2025-04-13 13:12:48 +03:00
6493f02bcc simplify if statements readabaility. 2025-04-13 13:08:23 +03:00
c7c8898763 removed redundant code. 2025-04-13 12:52:38 +03:00
62ee9a72d6 moved SidebarHeader and SidebarAddCommunityButton to their own files. 2025-04-13 12:47:05 +03:00
55695ca5db refactor: improve readability and structure in SidebarWidget's space tile handling 2025-04-13 12:33:50 +03:00
c2f5a8df10 refactor: streamline context usage and improve readability in SidebarWidget 2025-04-13 12:31:17 +03:00
cd9821679e added trailing commas. 2025-04-13 12:29:38 +03:00
bfd3d4542e refactor: simplify onSearchChanged callback to use expressions. 2025-04-13 12:28:50 +03:00
35a99ccda7 removed unnecessary comments from SidebarWidget. 2025-04-13 12:27:04 +03:00
978934399e fixed invalid use of a private type in a public API in SidebarWidget. 2025-04-13 12:25:28 +03:00
bc32fe7941 removed unused method and its use. 2025-04-13 12:25:03 +03:00
cb6d50d367 remove unnecessary print statement 2025-04-13 12:21:35 +03:00
97eb1c152b Implement a countdown timer for the AC and fix bugs in the 'Forgot Password' 2025-04-13 12:18:56 +03:00
11feaf7c87 passed correct operation name to onSliderChanged and onConditionChanged. in CpsDialogSliderSelector`. 2025-04-13 11:10:51 +03:00
36e88d033b Refactor SliderValueSelector layout for improved readability and maintainability 2025-04-13 10:56:19 +03:00
c54fd780b7 Update sensitivity function to adjust max value and add image assets; modify dialog logic for sensitivity handling 2025-04-13 10:43:17 +03:00
fa8f29ff71 Add sensitivity feature. 2025-04-13 10:41:23 +03:00
dcbca64814 Fix spacing in ValueDisplay text output for improved readability 2025-04-13 10:16:20 +03:00
c62e8b3f15 Update _displayText method to format values with two decimal places for improved precision 2025-04-13 10:14:35 +03:00
60a1a9ad6f Add dividendOfRange parameter to FunctionSlider and related components for improved range handling 2025-04-13 10:09:39 +03:00
828db5d5e4 Refactor CpsDialogSliderSelector to improve value parsing and formatting in _displayText method 2025-04-13 09:57:45 +03:00
ebbecb9738 Update toggleCodes in CeilingSensorHelper to replace 'self_test_result' and 'movement' with 'checking_result' and 'body_movement' for improved clarity. 2025-04-13 09:18:09 +03:00
49439816d5 Update SliderValueSelector to improve text styling for unit and range labels 2025-04-10 16:43:33 +03:00
a5d26d04eb Remove unused variables from CeilingSensorDialog to streamline state management 2025-04-10 16:35:19 +03:00
74046c5aed Refactor SliderValueSelector and ValueDisplay to include unit handling and improve UI consistency across sensor dialogs. 2025-04-10 16:22:02 +03:00
fadb23d631 Refactor SliderValueSelector layout by removing unnecessary SizedBox and adjusting spacing for improved UI consistency. 2025-04-10 15:26:36 +03:00
cf103d5a7c Add CpsDialogSliderSelector for enhanced slider functionality and integrate with CeilingSensorDialog 2025-04-10 15:25:29 +03:00
551779c33a moved widgets to their own files. 2025-04-10 14:41:58 +03:00
9d3b58deeb Refactor wall presence sensor components and add new widgets for improved functionality and clarity to ensure reusability in the future for other devices. 2025-04-10 14:32:09 +03:00
9ca6fb8640 Remove unnecessary extension methods for operational value codes in CPS functions 2025-04-10 14:03:44 +03:00
796409600e Update operation codes for CpsPresenceJudgementThresholdFunction and CpsMotionAmplitudeTriggerThresholdFunction. 2025-04-10 14:03:08 +03:00
75b2f96428 added spaces in operational value descriptions to match figma design. 2025-04-10 11:27:02 +03:00
7c68b90aef Refactor CPS functions to implement operational value generation with min, max, and step parameters. 2025-04-10 10:59:43 +03:00
729080ec4e Refactor CPS function codes to use helper methods for consistency and clarity 2025-04-10 10:11:12 +03:00
627ff4e929 Update operation codes and values for various CPS functions. 2025-04-09 17:00:21 +03:00
6d612398ed Update operation names in CpsFunctions and add toggle codes to CeilingSensorHelper 2025-04-09 16:41:03 +03:00
3f565788d5 Refactor CeilingSensorDialog to be a StatefulWidget. 2025-04-09 15:42:22 +03:00
120ed85d10 Add CPS case to device function switch for ceiling sensor integration 2025-04-09 15:41:28 +03:00
c1d9846899 Enhance CeilingSensorHelper: Add deviceName and deviceId parameters to sensor functions 2025-04-09 15:41:21 +03:00
cd9ed5861a Implemented some operations for the cps feature. 2025-04-09 15:40:44 +03:00
c0662bb19e Added assets. 2025-04-09 15:23:56 +03:00
5b29228738 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1275-FE-Identify-Add-Ceiling-Sensor-Card-to-IF-THEN-Sections 2025-04-09 13:21:43 +03:00
c23176706f Merge pull request #136 from SyncrowIOT/SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions
Implementing the Ceiling Sensor in Routine WEB
2025-04-09 13:19:53 +03:00
4ea91b9333 SP-1276/ Show Configuration Pop-up on Drop. 2025-04-09 13:16:36 +03:00
6c691d4b3c displays ceiling sensor in routine 2025-04-09 12:46:43 +03:00
387ef99728 fix: handle nullable uuid in GatewayModel.fromJson 2025-04-09 12:33:20 +03:00
694a5a7dda Refactor GatewayModel.fromJson to use a loop for status parsing instead of firstWhere. 2025-04-09 12:32:44 +03:00
c90b9a1912 bugfix/ range index errorof automations in routines. 2025-04-09 12:24:59 +03:00
7860292276 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions 2025-04-09 12:19:53 +03:00
550acc4953 Merge pull request #137 from SyncrowIOT/fix-remove-option-from-then-dialog-routins
add dialogType to devices and add parameter in showSwitchFunctionsDialog
2025-04-09 12:18:33 +03:00
fd6b737556 Refactor one_gang_switch_dialog.dart to use one_gang_switch instead of ac in function names and variables 2025-04-09 12:17:26 +03:00
3d5825adca Merge branch 'SP-1366-FE-Add-Gateway-Device-Card-to-Devices-Section' of https://github.com/SyncrowIOT/web into SP-1367-FE-Save-Flow-edit-or-delete-the-device-functions 2025-04-09 11:21:20 +03:00
9c97e2879a Refactor constructor syntax for Gateway functions to use initializer list 2025-04-09 10:57:22 +03:00
fda96025e9 uncommented code. 2025-04-09 10:41:38 +03:00
92aa574944 update GatewayMasterState.getOperationalValues to match what the api expects. 2025-04-09 10:21:49 +03:00
d08ab8caac add dialogType to devices and add parameter in showSwitchFunctionsDialog 2025-04-09 10:09:56 +03:00
774f21a55b Refactor RoutineDevices to consolidate device data preparation in a single map to remove code duplication. 2025-04-09 09:59:15 +03:00
9b69ec31e9 Refactor RoutineDevices to use a class-level constant for allowed product types. 2025-04-09 09:57:18 +03:00
a242377ea6 Fully refactored CreateRoutine/Gateway feature. 2025-04-09 09:46:45 +03:00
144 changed files with 7435 additions and 3221 deletions

View File

@ -25,13 +25,13 @@ jobs:
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2' # Specify the Flutter version you want to use
flutter-version: '3.27.3' # Specify the Flutter version you want to use
- name: Install dependencies
run: flutter pub get
- name: Build Flutter Web App
run: flutter build web --release --dart-define=FLAVOR=production
run: flutter build web --web-renderer canvaskit -t lib/main.dart
- name: Build And Deploy
id: builddeploy

View File

@ -25,13 +25,13 @@ jobs:
- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.22.2' # Specify the Flutter version you want to use
flutter-version: '3.27.3' # Specify the Flutter version you want to use
- name: Install dependencies
run: flutter pub get
- name: Build Flutter Web App
run: flutter build web --release --dart-define=FLAVOR=development
run: flutter build web --web-renderer canvaskit -t lib/main_dev.dart
- name: Build And Deploy
id: builddeploy

33
.vscode/launch.json vendored
View File

@ -10,11 +10,12 @@
"type": "dart",
"args": [
"--dart-define",
"FLAVOR=development"
"-d",
"chrome",
"--web-port",
"3000",
"-t",
"lib/main_dev.dart",
],
"flutterMode": "debug"
@ -28,11 +29,12 @@
"type": "dart",
"args": [
"--dart-define",
"FLAVOR=staging"
"-d",
"chrome",
"--web-port",
"3000",
"-t",
"lib/main_staging.dart",
],
"flutterMode": "debug"
@ -46,11 +48,12 @@
"type": "dart",
"args": [
"--dart-define",
"FLAVOR=production"
"-d",
"chrome",
"--web-port",
"3000",
"-t",
"lib/main.dart",
],
"flutterMode": "debug"

View File

@ -16,7 +16,12 @@ samples, guidance on mobile development, and a full API reference.
## USEFUL COMMANDS
Run on chrome: flutter run -d chrome --dart-define=FLAVOR='ENV_NAME'
- Building for the Web
- CanvasKit
- `flutter build web --web-renderer canvaskit -t lib/main_dev.dart --output=build/web_dev` - build for DEVELOPMENT.
- `flutter build web --web-renderer canvaskit -t lib/main_staging.dart --output=build/web_stg` - build for STAGING.
- `flutter build web --web-renderer canvaskit -t lib/main.dart --output=build/web` - build for PRODUCTION.
- run command: `flutter run -d chrome --target=lib/main_dev.dart`
Build: flutter build web --release --dart-define=FLAVOR='ENV_NAME'

28
assets/icons/boundary.svg Normal file
View File

@ -0,0 +1,28 @@
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.45051 0.620117H2.66797C2.76403 0.620117 2.84185 0.697982 2.84185 0.794001V16.5102H3.62439V0.794043C3.62439 0.697982 3.54657 0.620117 3.45051 0.620117Z" fill="#9C9C9C"/>
<path d="M19.39 0.620117H18.6074C18.7035 0.620117 18.7813 0.697982 18.7813 0.794001V16.5102H19.5638V0.794043C19.5638 0.697982 19.486 0.620117 19.39 0.620117Z" fill="#9C9C9C"/>
<path d="M2.84259 0.794001C2.84259 0.697982 2.76477 0.620117 2.66871 0.620117H2.2774C2.18134 0.620117 2.10352 0.697982 2.10352 0.794001V16.5102H2.84259V0.794001Z" fill="#ACACAC"/>
<path d="M18.7811 0.794001C18.7811 0.697982 18.7032 0.620117 18.6072 0.620117H18.2159C18.1198 0.620117 18.042 0.697982 18.042 0.794001V16.5102H18.7811V0.794001Z" fill="#ACACAC"/>
<path d="M7.55203 6.30395L11.8047 2.05127H8.21845L3.96582 6.30395H7.55203Z" fill="#FDB441"/>
<path d="M11.4857 6.30395L15.7383 2.05127H11.8035L7.55078 6.30395H11.4857Z" fill="#5A5A5A"/>
<path d="M15.0726 6.30395L19.3253 2.05127H15.739L11.4863 6.30395H15.0726Z" fill="#FDB441"/>
<path d="M4.28295 2.05127H0.378083C0.186003 2.05127 0.0302734 2.207 0.0302734 2.39908V5.95618C0.0302734 6.0522 0.0692057 6.13916 0.132132 6.20209L4.28295 2.05127Z" fill="#FDB441"/>
<path d="M4.28261 2.05127L0.131836 6.20209C0.194762 6.26501 0.281725 6.30395 0.377786 6.30395H3.96485L8.21748 2.05127H4.28261Z" fill="#5A5A5A"/>
<path d="M20.6996 4.61246V2.09067L20.2053 2.05461C20.1904 2.05262 20.1752 2.05127 20.1597 2.05127H19.3259L15.0732 6.30395H19.0081C19.3229 5.98914 20.3693 4.94271 20.6996 4.61246Z" fill="#5A5A5A"/>
<path d="M19.0078 6.30374H20.1596C20.1942 6.30374 20.2276 6.2985 20.2592 6.28906L20.6993 6.22419V4.6123C20.3688 4.94276 19.3225 5.98907 19.0078 6.30374Z" fill="#FDB441"/>
<path d="M21.6207 2.29561C21.6105 2.26294 21.5957 2.23235 21.5769 2.20459C21.5144 2.11208 21.4086 2.05127 21.2886 2.05127C21.2344 2.05127 20.8258 2.05127 20.1582 2.05127C20.3503 2.05127 20.506 2.20696 20.506 2.39908V3.67415V4.8045L21.6364 3.67415V2.39908C21.6364 2.36298 21.6309 2.32828 21.6207 2.29561Z" fill="#444444"/>
<path d="M20.5059 5.9563C20.5059 6.14834 20.3502 6.30407 20.1582 6.30411H21.2885C21.3365 6.30411 21.3823 6.29437 21.4239 6.27677C21.5331 6.23056 21.6134 6.13001 21.6319 6.00911C21.6345 5.99185 21.6363 5.97428 21.6363 5.9563V3.67432L20.506 4.80466V5.9563H20.5059Z" fill="#F6AB31"/>
<path d="M7.55203 11.782L11.8047 7.5293H8.21845L3.96582 11.782H7.55203Z" fill="#FDB441"/>
<path d="M11.4857 11.782L15.7383 7.5293H11.8035L7.55078 11.782H11.4857Z" fill="#5A5A5A"/>
<path d="M15.0726 11.782L19.3253 7.5293H15.739L11.4863 11.782H15.0726Z" fill="#FDB441"/>
<path d="M4.28295 7.5293H0.378083C0.186003 7.5293 0.0302734 7.68503 0.0302734 7.87711V11.4342C0.0302734 11.5302 0.0692057 11.6172 0.132132 11.6801L4.28295 7.5293Z" fill="#FDB441"/>
<path d="M4.28261 7.52934L0.131836 11.6801C0.194762 11.743 0.281725 11.782 0.377786 11.782H3.96485L8.21752 7.5293H4.28261V7.52934Z" fill="#5A5A5A"/>
<path d="M20.6996 10.0905V7.56869L20.2053 7.53264C20.1904 7.53065 20.1752 7.5293 20.1597 7.5293H19.3259L15.0732 11.782H19.0081C19.3229 11.4672 20.3693 10.4207 20.6996 10.0905Z" fill="#5A5A5A"/>
<path d="M19.0078 11.7818H20.1596C20.1942 11.7818 20.2276 11.7766 20.2592 11.7671L20.6993 11.7022V10.0903C20.3688 10.4208 19.3225 11.4671 19.0078 11.7818Z" fill="#FDB441"/>
<path d="M21.6207 7.77364C21.6105 7.74097 21.5957 7.71037 21.5769 7.68261C21.5144 7.59011 21.4086 7.5293 21.2886 7.5293C21.2344 7.5293 20.8258 7.5293 20.1582 7.5293C20.3503 7.5293 20.506 7.68498 20.506 7.87711V9.15218V10.2825L21.6364 9.15218V7.87711C21.6364 7.84105 21.6309 7.80631 21.6207 7.77364Z" fill="#444444"/>
<path d="M20.5059 11.4343C20.5059 11.6264 20.3502 11.7821 20.1582 11.7821H21.2885C21.3365 11.7821 21.3823 11.7724 21.4239 11.7548C21.5331 11.7086 21.6134 11.608 21.6319 11.4871C21.6345 11.4699 21.6363 11.4523 21.6363 11.4343V9.15234L20.506 10.2827V11.4343H20.5059Z" fill="#F6AB31"/>
<path d="M5.46624 15.6406H4.42285C4.5669 15.6406 4.6837 15.7574 4.6837 15.9015V17.1188C4.6837 17.2628 4.5669 17.3796 4.42285 17.3796H5.46624C5.61029 17.3796 5.72708 17.2628 5.72708 17.1188V15.9015C5.72708 15.7574 5.61029 15.6406 5.46624 15.6406Z" fill="#ACACAC"/>
<path d="M21.4057 17.3796C21.5497 17.3796 21.6665 17.2628 21.6665 17.1188V15.9015C21.6665 15.7574 21.5497 15.6406 21.4057 15.6406H20.3623C20.5064 15.6406 20.6232 15.7574 20.6232 15.9015V17.1188C20.6232 17.2628 20.5064 17.3796 20.3623 17.3796H21.4057Z" fill="#ACACAC"/>
<path d="M0.260846 15.6406C0.116797 15.6406 0 15.7574 0 15.9015V17.1188C0 17.2628 0.116797 17.3796 0.260846 17.3796H4.4236C4.56765 17.3796 4.6844 17.2628 4.6844 17.1188V15.9015C4.6844 15.7574 4.5676 15.6406 4.42355 15.6406H0.260846Z" fill="#BFBFBF"/>
<path d="M16.2003 15.6406C16.0563 15.6406 15.9395 15.7574 15.9395 15.9015V17.1188C15.9395 17.2628 16.0563 17.3796 16.2003 17.3796H20.363C20.5071 17.3796 20.6239 17.2628 20.6239 17.1188V15.9015C20.6239 15.7574 20.5071 15.6406 20.363 15.6406H16.2003Z" fill="#BFBFBF"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,10 @@
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.97266 12.0879H17.8775" stroke="#373737" stroke-linecap="round"/>
<path d="M3.97266 11.5879C3.69651 11.5879 3.47266 11.8117 3.47266 12.0879C3.47266 12.364 3.69651 12.5879 3.97266 12.5879V11.5879ZM11.9183 12.5879H12.4183V11.5879H11.9183V12.5879ZM3.97266 12.5879H11.9183V11.5879H3.97266V12.5879Z" fill="#75777F"/>
<path d="M3.99488 8.50684C1.71745 8.50684 0 9.54938 0 10.9319C0 12.3144 1.71745 13.3569 3.99488 13.3569C6.27232 13.3569 7.98976 12.3144 7.98976 10.9319C7.98976 9.54938 6.27236 8.50684 3.99488 8.50684Z" fill="#FFCC03"/>
<path d="M3.99488 8.50684C1.71745 8.50684 0 9.54938 0 10.9319C0 12.3144 1.71745 13.3569 3.99488 13.3569V8.50684Z" fill="#FFE981"/>
<path d="M6.90323 2.27066C6.24679 1.30901 5.15979 0.734863 3.99547 0.734863C2.83114 0.734863 1.74414 1.30897 1.0877 2.27066C0.431304 3.23231 0.292645 4.45382 0.716885 5.53815L2.58009 10.3005C2.80969 10.8874 3.36522 11.2667 3.99547 11.2667C4.62572 11.2667 5.18124 10.8874 5.41084 10.3005L7.27405 5.53811C7.69829 4.45382 7.55963 3.23231 6.90323 2.27066Z" fill="#CC1F2C"/>
<path d="M3.99449 0.734863C2.83013 0.734863 1.74316 1.30897 1.08673 2.27066C0.430327 3.23231 0.291669 4.45382 0.715909 5.53815L2.57911 10.3005C2.80871 10.8874 3.36424 11.2667 3.99449 11.2667V0.734863Z" fill="#F05449"/>
<path d="M19.5562 5.2553C19.0806 4.55855 18.293 4.14258 17.4494 4.14258C16.6058 4.14258 15.8182 4.55855 15.3426 5.2553C14.8671 5.95205 14.7666 6.83704 15.074 7.62267L16.4239 11.0731C16.5903 11.4983 16.9928 11.7731 17.4494 11.7731C17.906 11.7731 18.3086 11.4983 18.4749 11.0731L19.8248 7.62263C20.1322 6.83708 20.0317 5.95205 19.5562 5.2553Z" fill="#CC1F2C"/>
<path d="M17.4494 4.14258C16.6058 4.14258 15.8182 4.55855 15.3426 5.2553C14.8671 5.95205 14.7666 6.83704 15.074 7.62267L16.4239 11.0731C16.5903 11.4983 16.9928 11.7731 17.4494 11.7731V4.14258Z" fill="#F05449"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,22 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.72272 14.6122L6.18327 14.354L2.97852 19.0925H6.55507L6.89972 18.8677V14.7744L6.72272 14.6122Z" fill="#407093"/>
<path d="M6.23049 14.3775L5.01761 13.7969H4.78901L1.35938 18.8679L1.56355 19.0926H3.04156L6.23049 14.3775Z" fill="#365E7D"/>
<path d="M7.54213 18.8677H2.81285C2.59801 18.8677 2.42383 19.0418 2.42383 19.2567V19.7497C2.42383 19.9645 2.59797 20.1386 2.81277 20.1386H7.54213C7.75698 20.1386 7.93116 19.9645 7.93116 19.7497V19.2567C7.93116 19.0418 7.75698 18.8677 7.54213 18.8677Z" fill="#4A80AA"/>
<path d="M2.50507 19.7497V19.2567C2.50507 19.0419 2.67925 18.8677 2.89409 18.8677H0.389022C0.174179 18.8677 0 19.0418 0 19.2567V19.7497C0 19.9645 0.174179 20.1386 0.389022 20.1386H2.89405C2.67921 20.1386 2.50507 19.9645 2.50507 19.7497Z" fill="#407093"/>
<path d="M6.06769 11.5625C5.92211 11.8628 5.83984 12.1995 5.83984 12.5557C5.83984 13.5291 6.44937 14.3595 7.30726 14.688C7.67558 14.5726 8.00285 14.3644 8.26136 14.0903V13.593L6.06769 11.5625Z" fill="#4A80AA"/>
<path d="M5.88003 12.5635C5.88003 12.2155 5.9605 11.8866 6.10296 11.5934L5.40175 10.9443H5.11722C4.68695 11.3502 4.41797 11.9253 4.41797 12.5635C4.41797 13.7929 5.41461 14.7896 6.64406 14.7896C6.90015 14.7896 7.14573 14.7456 7.37464 14.666C6.50464 14.3633 5.88003 13.5366 5.88003 12.5635Z" fill="#407093"/>
<path d="M18.8025 0.40287C18.4502 0.0506057 17.8791 0.0506057 17.5268 0.40287L16.666 1.26369V1.37279L17.7404 2.49283L17.9416 2.53931L18.8024 1.67849C19.1547 1.32627 19.1547 0.755135 18.8025 0.40287Z" fill="#FFD086"/>
<path d="M19.0404 0.82865C19.0028 0.672713 18.9242 0.524628 18.8025 0.40287C18.4502 0.0506057 17.8791 0.0506057 17.5268 0.40287L16.666 1.26369V1.37279L17.2357 1.96662C17.5753 1.61322 17.9125 1.26447 18.0859 1.09107C18.3749 0.802049 18.7971 0.799822 19.0404 0.82865Z" fill="#FFC365"/>
<path d="M15.8571 2.69561C15.8473 2.69561 15.8373 2.6951 15.8272 2.69413L5.87968 1.71499C5.71378 1.69866 5.59257 1.55097 5.6089 1.38507C5.62522 1.21917 5.773 1.09804 5.93882 1.11425L15.8864 2.09335C16.0522 2.10967 16.1735 2.25737 16.1571 2.42327C16.1418 2.57917 16.0105 2.69561 15.8571 2.69561Z" fill="#365E7D"/>
<path d="M8.75675 4.55789C8.62288 4.55789 8.50058 4.46812 8.46503 4.33258C8.42273 4.17133 8.51917 4.00633 8.68038 3.96402L15.7804 2.10172C15.9416 2.0595 16.1066 2.15586 16.1489 2.31711C16.1912 2.47832 16.0948 2.64332 15.9336 2.68563L8.83355 4.54793C8.80784 4.55465 8.78206 4.55789 8.75675 4.55789Z" fill="#407093"/>
<path d="M17.7843 13.6061C17.6308 13.6061 17.4995 13.4895 17.4843 13.3336L16.5112 3.37993C16.495 3.21407 16.6163 3.06641 16.7822 3.05016C16.9481 3.0347 17.0957 3.15532 17.112 3.32122L18.085 13.2749C18.1012 13.4407 17.9799 13.5884 17.814 13.6046C17.804 13.6056 17.7941 13.6061 17.7843 13.6061Z" fill="#365E7D"/>
<path d="M14.9212 10.781C14.8956 10.781 14.8696 10.7777 14.8437 10.7709C14.6825 10.7281 14.5866 10.5629 14.6293 10.4018L16.519 3.27433C16.5617 3.11315 16.7269 3.01733 16.8881 3.05991C17.0492 3.10265 17.1452 3.26788 17.1025 3.42901L15.2127 10.5565C15.1769 10.6917 15.0548 10.781 14.9212 10.781Z" fill="#407093"/>
<path d="M15.8027 2.12842L16.6657 1.26541L17.9412 2.54085L17.0782 3.40386L15.8027 2.12842Z" fill="#FFC365"/>
<path d="M16.4582 2.78031C16.6656 2.56312 16.9895 2.22437 17.3028 1.89867L16.6678 1.26367L15.8047 2.12679L16.4582 2.78031Z" fill="#FFA90F"/>
<path d="M17.2722 3.89628C17.195 3.89628 17.1177 3.86679 17.0588 3.80788L15.399 2.14805C15.2811 2.03016 15.2811 1.8391 15.399 1.72117C15.5168 1.60336 15.7079 1.60336 15.8258 1.72117L17.4857 3.38101C17.6036 3.4989 17.6036 3.68995 17.4857 3.80788C17.4267 3.86683 17.3495 3.89628 17.2722 3.89628Z" fill="#4A80AA"/>
<path d="M17.4668 14.1492L7.36085 3.95557C6.02101 6.98032 5.78835 10.2274 6.7914 13.0274C9.56154 15.5262 13.6996 15.9099 17.5091 14.3392L17.4668 14.1492Z" fill="#B5DCFF"/>
<path d="M7.4139 4.01031L5.09848 1.6748L4.86645 1.6977C3.23512 5.65405 3.71145 9.96509 6.47621 12.7299C6.64367 12.8973 6.81695 13.0562 6.99535 13.207C5.82988 10.4102 6.01929 7.08393 7.4139 4.01031Z" fill="#8BCAFF"/>
<path d="M18.3911 13.4249L8.14156 3.17529C8.0407 3.19084 7.94359 3.2367 7.86594 3.31435L7.42012 3.76021C7.32437 3.85596 7.27676 3.98099 7.27539 4.1065L17.4925 14.3235C17.6444 14.4755 17.8907 14.4755 18.0426 14.3235L18.3912 13.975C18.5431 13.8231 18.5431 13.5768 18.3911 13.4249Z" fill="#DBEDFF"/>
<path d="M7.4351 3.76805L7.87162 3.33153C7.95201 3.25114 8.05354 3.20512 8.1583 3.19266L5.78221 0.816572C5.6303 0.664658 5.38401 0.664658 5.23206 0.816572L4.88347 1.16516C4.73155 1.31708 4.73155 1.56337 4.88347 1.71528L7.29295 4.12477C7.28979 3.99602 7.33686 3.86629 7.4351 3.76805Z" fill="#B5DCFF"/>
<path d="M14.5 10.9887C11.9318 10.9887 9.85 13.0705 9.85 15.6387C9.85 18.2068 11.9318 20.2887 14.5 20.2887C17.0682 20.2887 19.15 18.2068 19.15 15.6387C19.15 13.0705 17.0682 10.9887 14.5 10.9887ZM10.9594 15.6387C10.9594 14.4992 11.4977 13.4855 12.3344 12.8377L15.6926 18.9731C15.3201 19.1064 14.9187 19.1792 14.5 19.1792C12.5446 19.1793 10.9594 17.5941 10.9594 15.6387ZM18.0404 15.6387C18.0404 16.7782 17.5021 17.7919 16.6655 18.4397L13.3073 12.3042C13.6798 12.1709 14.0813 12.0981 14.5 12.0981C16.4554 12.0981 18.0404 13.6833 18.0404 15.6387Z" fill="#FF5A73" stroke="#FF5A73" stroke-width="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -0,0 +1,15 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.37727 0.166504L0 4.54378L17.2893 21.8331L21.6666 17.4557L4.37727 0.166504ZM3.1988 4.37544C2.91985 4.09649 2.91985 3.64426 3.1988 3.3653C3.47775 3.08635 3.92999 3.08635 4.20894 3.3653C4.48789 3.64426 4.48789 4.09649 4.20894 4.37544C3.92999 4.6544 3.47775 4.6544 3.1988 4.37544ZM17.4576 18.6343C17.1787 18.3553 17.1787 17.9031 17.4576 17.6241C17.7366 17.3452 18.1888 17.3452 18.4678 17.6241C18.7467 17.9031 18.7467 18.3553 18.4678 18.6343C18.1889 18.9132 17.7366 18.9132 17.4576 18.6343Z" fill="#98D9D5"/>
<path d="M8.72863 7.2116C8.58915 7.35108 8.58915 7.57717 8.72863 7.71669C8.86811 7.85622 9.0942 7.85617 9.23372 7.71669L10.5806 6.36984L10.0755 5.86475L8.72863 7.2116Z" fill="#6DA8D6"/>
<path d="M5.80871 4.29119C5.66923 4.43067 5.66923 4.65676 5.80871 4.79628C5.94818 4.93581 6.17428 4.93576 6.3138 4.79628L7.66066 3.44943L7.15556 2.94434L5.80871 4.29119Z" fill="#6DA8D6"/>
<path d="M7.26867 5.75164C7.12919 5.89111 7.12919 6.11721 7.26867 6.25673C7.40815 6.39621 7.63424 6.39621 7.77376 6.25673L9.12062 4.90988L8.61552 4.40479L7.26867 5.75164Z" fill="#6DA8D6"/>
<path d="M14.1163 12.5993C13.9768 12.7388 13.9768 12.9649 14.1163 13.1044C14.2558 13.2439 14.4819 13.2439 14.6214 13.1044L15.9683 11.7575L15.4632 11.2524L14.1163 12.5993Z" fill="#6DA8D6"/>
<path d="M18.3841 14.1729L17.0372 15.5197C16.8977 15.6592 16.8977 15.8853 17.0372 16.0248C17.1767 16.1643 17.4028 16.1643 17.5423 16.0248L18.8892 14.6779L18.3841 14.1729Z" fill="#6DA8D6"/>
<path d="M15.5773 14.0597C15.4378 14.1992 15.4378 14.4253 15.5773 14.5648C15.7167 14.7043 15.9428 14.7043 16.0824 14.5648L17.4292 13.218L16.9241 12.7129L15.5773 14.0597Z" fill="#6DA8D6"/>
<path d="M21.25 1.93008L19.9031 0.583181C19.3475 0.0276114 18.4384 0.0276114 17.8828 0.583181L16.1992 2.2668L17.5461 4.28708L19.5664 5.63393L21.25 3.95031C21.8056 3.39479 21.8056 2.48565 21.25 1.93008Z" fill="#F2484B"/>
<path d="M14.9445 3.52148L2.35938 16.1066L3.36728 18.4659L17.3015 5.2051L14.9445 3.52148Z" fill="#6DA8D6"/>
<path d="M16.9637 4.86816L3.36621 18.4656L5.72549 19.4736L18.3106 6.88849L16.9637 4.86816Z" fill="#185F8D"/>
<path d="M0 21.8333L2.38509 20.8512L2.02028 19.8131L0.982092 19.4482L0 21.8333Z" fill="#274B6D"/>
<path d="M5.72586 19.4736C5.64581 19.1756 5.48467 18.8876 5.24353 18.6465C4.81015 18.2131 4.22577 18.0372 3.70329 18.129C3.7951 17.6065 3.6192 17.0222 3.18582 16.5888C2.94468 16.3476 2.65668 16.1865 2.35868 16.1064L2.35639 16.1087L0.981445 19.4478L2.38444 20.8508L5.72353 19.4759L5.72586 19.4736Z" fill="#FFCC75"/>
<path d="M16.1991 2.26718L14.9443 3.52197L18.3115 6.88911L19.5663 5.63431L16.1991 2.26718Z" fill="#F2EBD9"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,6 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20.0156C15.5228 20.0156 20 15.5385 20 10.0156C20 4.49278 15.5228 0.015625 10 0.015625C4.47715 0.015625 0 4.49278 0 10.0156C0 15.5385 4.47715 20.0156 10 20.0156Z" fill="#FFB54A"/>
<path d="M13.3263 19.4481C16.3853 18.3692 18.762 15.8448 19.636 12.6949L11.4992 4.55811C11.4992 4.55811 8.03387 7.46049 8.17614 8.25693L9.7659 9.82752L9.23356 15.3554L13.3263 19.4481Z" fill="#F9880D"/>
<path d="M11.4996 4.55811V15.3554H9.23398V7.34521L8.17656 8.25693L6.69727 6.54092L8.86055 4.67568L8.99688 4.55811H11.4996Z" fill="#F8FFFB"/>
<path d="M10 4.55811H11.4996V15.3554H10V4.55811Z" fill="#D8D8D8"/>
</svg>

After

Width:  |  Height:  |  Size: 705 B

View File

@ -0,0 +1,7 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20.0464C15.5228 20.0464 20 15.5692 20 10.0464C20 4.52354 15.5228 0.0463867 10 0.0463867C4.47715 0.0463867 0 4.52354 0 10.0464C0 15.5692 4.47715 20.0464 10 20.0464Z" fill="#424242"/>
<path d="M11.0775 19.9883C15.3757 19.5277 18.8585 16.3439 19.7673 12.1961L13.3194 5.74813C13.3194 5.74813 8.04414 5.60235 6.13867 8.73633L8.9466 11.5514L6.44617 15.3861L11.0775 19.9883Z" fill="#232323"/>
<path d="M14.159 15.3864H6.44613V12.7829L11.3996 9.03281C12.0277 8.55727 11.8763 7.89418 11.815 7.70227C11.7559 7.51742 11.5043 6.91242 10.7484 6.89035C10.7315 6.88988 10.7019 6.88961 10.69 6.88953H9.81414C8.52395 6.88953 8.42371 8.30332 8.42371 8.73664H6.13867C6.13867 6.30367 7.65008 4.60449 9.81414 4.60449L10.7029 4.60453C10.7029 4.60453 10.7776 4.6052 10.8148 4.60625C12.2924 4.64941 13.5392 5.59156 13.9916 7.00645C14.4497 8.43957 13.9737 9.95004 12.7789 10.8546L9.81121 13.1014H14.1591V15.3864H14.159Z" fill="#F8FFFB"/>
<path d="M12.7792 10.8544L10.1514 12.844V9.97785L11.3999 9.0327C12.0279 8.55719 11.8766 7.8941 11.8155 7.70227C11.7564 7.51711 11.5046 6.91234 10.7486 6.89031C10.7317 6.88992 10.7021 6.88953 10.6903 6.88953H10.1514V4.60449H10.7033C10.7033 4.60449 10.7778 4.60527 10.8152 4.60605C12.2926 4.64938 13.5395 5.59137 13.9918 7.00652C14.45 8.43937 13.9741 9.94988 12.7792 10.8544Z" fill="#D8D8D8"/>
<path d="M10.1514 13.1011H14.1593V15.3861H10.1514V13.1011Z" fill="#D8D8D8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,6 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20.0771C15.5228 20.0771 20 15.6 20 10.0771C20 4.5543 15.5228 0.0771484 10 0.0771484C4.47715 0.0771484 0 4.5543 0 10.0771C0 15.6 4.47715 20.0771 10 20.0771Z" fill="#07E5CA"/>
<path d="M10.0839 13.4524L8.77727 12.2934L7.35418 14.2613L12.779 19.6856C16.1417 18.7146 18.7756 16.0263 19.6699 12.6317L12.7734 5.73535L6.46289 7.4725L9.23348 10.2371L9.09187 10.8674L11.2682 12.9721C11.2682 12.972 10.5204 13.5331 10.0839 13.4524Z" fill="#00B59B"/>
<path d="M12.7735 5.73498C12.1468 5.03658 11.1001 4.61963 9.97359 4.61963C9.74531 4.61963 9.51633 4.64061 9.29305 4.68197L9.22426 4.69467C8.53879 4.82131 7.91781 5.13639 7.42836 5.60576C6.94879 6.066 6.6284 6.64404 6.5018 7.27736L6.46289 7.47209L8.77723 7.64721L8.8243 7.52557C8.95844 7.17908 9.2818 6.91943 9.66816 6.84799L9.73695 6.83529C10.1734 6.75467 10.6217 6.90189 10.907 7.21986C11.2853 7.64131 11.2475 8.31838 10.8245 8.69842C10.6195 8.88256 9.61078 9.09518 9.27523 9.08119L9.09191 9.07334V10.867L9.27523 10.8592C9.61301 10.845 10.6196 11.0578 10.8245 11.2419C11.2475 11.622 11.2853 12.299 10.907 12.7205C10.6217 13.0385 10.1734 13.1857 9.73691 13.1051L9.6682 13.0924C9.2818 13.0209 8.95844 12.7613 8.8243 12.4148L8.77723 12.2931L6.46289 12.4683L6.5018 12.663C6.62836 13.2963 6.94875 13.8744 7.4284 14.3346C7.91777 14.804 8.53879 15.119 9.22426 15.2457L9.29297 15.2584C9.51633 15.2997 9.74531 15.3207 9.97359 15.3207C11.1001 15.3207 12.1468 14.9038 12.7734 14.2054C13.8294 13.0288 13.7796 11.1994 12.6833 9.97018C13.7796 8.74092 13.8294 6.91158 12.7735 5.73498Z" fill="#F8FFFB"/>
<path d="M12.6836 9.97012C13.7801 11.1994 13.8297 13.0287 12.7738 14.2057C12.152 14.8982 11.1168 15.3143 10 15.3205V13.1264C10.3469 13.1197 10.6797 12.9748 10.9074 12.7205C11.2855 12.299 11.248 11.6221 10.825 11.242C10.7117 11.1404 10.3543 11.0299 10 10.9537V8.98652C10.3543 8.91035 10.7117 8.8002 10.825 8.69863C11.248 8.31855 11.2855 7.6416 10.9074 7.22012C10.6797 6.96582 10.3469 6.8209 10 6.81426V4.62012C11.1168 4.62637 12.152 5.04199 12.7738 5.73496C13.8297 6.91152 13.7801 8.74082 12.6836 9.97012Z" fill="#D8D8D8"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,6 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 20.1079C15.5228 20.1079 20 15.6308 20 10.1079C20 4.58506 15.5228 0.10791 10 0.10791C4.47715 0.10791 0 4.58506 0 10.1079C0 15.6308 4.47715 20.1079 10 20.1079Z" fill="#424242"/>
<path d="M19.8765 11.6809L12.9637 4.76807L5.99414 12.7653L12.907 19.6782C16.525 18.5806 19.2749 15.4882 19.8765 11.6809Z" fill="#232323"/>
<path d="M12.9637 10.4997V4.76807H10.0215L5.99414 10.5458V12.7653H10.698V15.4478H12.9637V12.7653H14.4117V10.4997H12.9637ZM10.698 10.4997H8.78789L10.698 7.75986V10.4997Z" fill="#F8FFFB"/>
<path d="M12.9637 10.4997V4.76807H10.0215L10 4.79893V8.76104L10.698 7.75986V10.4997H10V12.7653H10.698V15.4478H12.9637V12.7653H14.4117V10.4997H12.9637Z" fill="#D8D8D8"/>
</svg>

After

Width:  |  Height:  |  Size: 787 B

View File

@ -0,0 +1,4 @@
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.13918 0.5H4.10294C4.0408 0.5 3.98117 0.524721 3.93722 0.568668L0.251783 4.25398C-0.0839278 4.58982 -0.0839278 5.13617 0.251783 5.47188L3.02848 8.24852C3.1906 8.41064 3.40686 8.49994 3.63734 8.5H3.6374C3.86794 8.5 4.0842 8.41064 4.24638 8.24846L7.9317 4.56308C7.97565 4.51914 8.00037 4.4595 8.00037 4.39736L8.00043 1.36113C8.00037 0.886312 7.61399 0.5 7.13918 0.5ZM7.53159 4.30031L3.91488 7.91702C3.84127 7.9907 3.74269 8.03122 3.6374 8.03122C3.53205 8.03122 3.43353 7.9907 3.35992 7.91708L0.583222 5.14045C0.43026 4.98748 0.43026 4.73845 0.583222 4.58542L4.19999 0.968775H7.13918C7.35556 0.968775 7.53165 1.14481 7.53165 1.36119L7.53159 4.30031Z" fill="#999999"/>
<path d="M5.93455 1.8291C5.73782 1.8291 5.55288 1.90577 5.41377 2.04487C5.27466 2.18392 5.19806 2.36886 5.19806 2.56559C5.19806 2.76232 5.27466 2.94726 5.41377 3.08637C5.55288 3.22548 5.73782 3.30208 5.93455 3.30208C6.13121 3.30208 6.31616 3.22548 6.45527 3.08637C6.59437 2.94726 6.67098 2.76232 6.67098 2.56559C6.67098 2.36886 6.59437 2.18392 6.45533 2.04487C6.31622 1.90577 6.13128 1.8291 5.93455 1.8291ZM6.12383 2.75487C6.07329 2.80547 6.00602 2.83331 5.93455 2.83331C5.86301 2.83331 5.79581 2.80547 5.74527 2.75487C5.69467 2.70433 5.66683 2.63707 5.66683 2.56559C5.66683 2.49412 5.69467 2.42685 5.74527 2.37631C5.79581 2.32571 5.86307 2.29788 5.93455 2.29788C6.00602 2.29788 6.07323 2.32571 6.12383 2.37631C6.17443 2.42685 6.20226 2.49412 6.20226 2.56559C6.20226 2.63707 6.17437 2.70433 6.12383 2.75487Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.7889 10.2691H11.3018C10.7286 10.2691 10.2622 9.80276 10.2622 9.22952V9.14261C10.2622 8.56937 10.7286 8.10301 11.3018 8.10301H17.4112C17.7348 8.10301 17.9971 7.84067 17.9971 7.51708C17.9971 7.19349 17.7348 6.93115 17.4112 6.93115H11.3018C10.0824 6.93115 9.09034 7.92321 9.09034 9.14261V9.22952C9.09034 10.4489 10.0824 11.441 11.3018 11.441H17.7889C18.3621 11.441 18.8285 11.9073 18.8285 12.4806C18.8285 13.0538 18.3621 13.5202 17.7889 13.5202H2.48241C2.15882 13.5202 1.89648 13.7825 1.89648 14.1061C1.89648 14.4297 2.15882 14.692 2.48241 14.692H17.7889C19.0083 14.692 20.0004 13.7 20.0004 12.4806C20.0004 11.2612 19.0083 10.2691 17.7889 10.2691Z" fill="#373737"/>
<path d="M11.3013 11.441H13.7698V10.2691H11.3013C10.7281 10.2691 10.2617 9.80276 10.2617 9.22952V9.14261C10.2617 8.56937 10.7281 8.10301 11.3013 8.10301H13.7698V6.93115H11.3013C10.0819 6.93115 9.08984 7.92321 9.08984 9.14261V9.22952C9.08984 10.4489 10.0819 11.441 11.3013 11.441Z" fill="#75777F"/>
<path d="M13.7693 13.52H2.48144C2.15785 13.52 1.89551 13.7824 1.89551 14.1059C1.89551 14.4295 2.15785 14.6919 2.48144 14.6919H13.7693V13.52Z" fill="#75777F"/>
<path d="M4.02221 10.8325C1.7292 10.8325 0 11.8822 0 13.2742C0 14.6661 1.7292 15.7158 4.02221 15.7158C6.31523 15.7158 8.04443 14.6661 8.04443 13.2742C8.04443 11.8822 6.31527 10.8325 4.02221 10.8325Z" fill="#FFCC03"/>
<path d="M4.02221 10.8325C1.7292 10.8325 0 11.8822 0 13.2742C0 14.6661 1.7292 15.7158 4.02221 15.7158V10.8325Z" fill="#FFE981"/>
<path d="M6.95014 4.55363C6.28921 3.5854 5.19477 3.00732 4.02248 3.00732C2.8502 3.00732 1.75576 3.58536 1.09483 4.55363C0.433937 5.52186 0.29433 6.75173 0.721473 7.84347L2.59742 12.6384C2.82859 13.2293 3.38792 13.6112 4.02248 13.6112C4.65705 13.6112 5.21638 13.2293 5.44754 12.6384L7.3235 7.84343C7.75064 6.75173 7.61103 5.52186 6.95014 4.55363Z" fill="#CC1F2C"/>
<path d="M4.02151 3.00732C2.84918 3.00732 1.75478 3.58536 1.09385 4.55363C0.432961 5.52186 0.293353 6.75173 0.720496 7.84347L2.59645 12.6384C2.82762 13.2293 3.38695 13.6112 4.02151 13.6112V3.00732Z" fill="#F05449"/>
<path d="M19.5529 1.55833C19.074 0.856811 18.2811 0.437988 17.4317 0.437988C16.5823 0.437988 15.7894 0.856811 15.3105 1.55833C14.8317 2.25984 14.7305 3.15088 15.04 3.94189L16.3992 7.41594C16.5667 7.84406 16.972 8.12074 17.4317 8.12074C17.8915 8.12074 18.2967 7.84406 18.4642 7.41594L19.8234 3.94185C20.1328 3.15092 20.0317 2.25984 19.5529 1.55833Z" fill="#CC1F2C"/>
<path d="M17.4317 0.437988C16.5823 0.437988 15.7894 0.856811 15.3105 1.55833C14.8317 2.25984 14.7305 3.15088 15.04 3.94189L16.3992 7.41594C16.5667 7.84406 16.972 8.12074 17.4317 8.12074V0.437988Z" fill="#F05449"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,10 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.8333 21.8332C16.8164 21.8332 21.6667 16.9829 21.6667 10.9998C21.6667 5.01675 16.8164 0.166504 10.8333 0.166504C4.85025 0.166504 0 5.01675 0 10.9998C0 16.9829 4.85025 21.8332 10.8333 21.8332Z" fill="#59ABFF"/>
<path d="M21.6663 10.9998C21.6663 5.0267 16.8061 0.166504 10.833 0.166504V21.8332C16.8061 21.8332 21.6663 16.973 21.6663 10.9998Z" fill="#4D87FF"/>
<path d="M10.8337 19.6663C15.6201 19.6663 19.5003 15.7861 19.5003 10.9997C19.5003 6.21321 15.6201 2.33301 10.8337 2.33301C6.04719 2.33301 2.16699 6.21321 2.16699 10.9997C2.16699 15.7861 6.04719 19.6663 10.8337 19.6663Z" fill="#F3F5F9"/>
<path d="M19.4997 10.9997C19.4997 6.22095 15.6117 2.33301 10.833 2.33301V19.6663C15.6117 19.6663 19.4997 15.7784 19.4997 10.9997Z" fill="#E1E6F0"/>
<path d="M14.4007 7.93541C13.6771 7.21182 12.1435 6.66618 10.8333 6.66618C10.235 6.66618 9.75 6.18113 9.75 5.58285C9.75 4.98456 10.235 4.49951 10.8333 4.49951C12.7149 4.49951 14.8118 5.2824 15.9327 6.40329C16.3558 6.82637 14.6121 8.14688 14.4007 7.93541Z" fill="#FF5959"/>
<path d="M14.4004 7.9359C14.6119 8.14744 16.3555 6.82693 15.9324 6.40385C14.8114 5.28289 12.7145 4.5 10.833 4.5V6.66667C12.1432 6.66667 13.6768 7.21231 14.4004 7.9359Z" fill="#E63A57"/>
<path d="M10.8338 12.0833C10.534 12.0833 10.2358 11.9596 10.0216 11.7174C9.62541 11.2691 9.66758 10.5845 10.1159 10.1882L14.4493 6.35808C14.8974 5.9618 15.5821 6.00405 15.9784 6.45233C16.3746 6.90061 16.3325 7.58528 15.8841 7.98149L11.5508 11.8117C11.3447 11.9938 11.0887 12.0833 10.8338 12.0833Z" fill="#4D4D80"/>
<path d="M14.4489 6.35808L10.833 9.55413V12.0833H10.8334C11.0884 12.0833 11.3444 11.9938 11.5505 11.8117L15.8838 7.98149C16.3322 7.58528 16.3743 6.90061 15.9781 6.45233C15.5817 6.00405 14.897 5.9618 14.4489 6.35808Z" fill="#443D66"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,9 @@
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.6665 12.1843H13.9647V11.5495C13.9647 8.39954 11.4019 5.8367 8.25187 5.8367C5.10188 5.8367 2.53904 8.39954 2.53904 11.5495V12.1843H0V11.5495C0 8.65592 1.12686 5.93539 3.17297 3.88928C5.21908 1.84317 7.93962 0.716309 10.8332 0.716309C13.7268 0.716309 16.4474 1.84317 18.4935 3.88928C20.5396 5.93539 21.6665 8.65592 21.6665 11.5495V12.1843Z" fill="#FFCE2E"/>
<path d="M18.4934 3.88928C16.4473 1.84317 13.7267 0.716309 10.8331 0.716309C7.93952 0.716309 5.21898 1.84317 3.17288 3.88928C3.02245 4.03954 2.87781 4.1941 2.7373 4.35146L5.1441 6.75826C6.03921 6.17574 7.10657 5.8367 8.25178 5.8367C10.8733 5.8367 13.0879 7.61188 13.7572 10.0233L21.6559 11.0867C21.5424 8.36681 20.4301 5.82596 18.4934 3.88928Z" fill="#FF9900"/>
<path d="M13.9645 11.5495V12.1843H21.6662V11.5495C21.6662 8.65592 20.5394 5.93539 18.4933 3.88928C16.4472 1.84317 13.7266 0.716309 10.833 0.716309V6.4541C12.6895 7.39847 13.9645 9.32788 13.9645 11.5495Z" fill="#FF4F18"/>
<path d="M18.967 3.89014L9.22326 12.7801C8.8667 13.1054 8.85381 13.6625 9.19515 14.0036L11.5747 16.3832C11.9164 16.725 12.4746 16.7116 12.7996 16.3537L21.6674 6.58985V3.89014H18.967Z" fill="#B7E4F8"/>
<path d="M11.5735 16.3832C11.9153 16.725 12.4736 16.7116 12.7985 16.3537L21.6663 6.58985V3.89014L10.3828 15.1925L11.5735 16.3832Z" fill="#6FC8F1"/>
<path d="M10.8328 17.2838C10.1546 17.2838 9.51683 17.0197 9.03729 16.5401C8.04746 15.5501 8.04746 13.9393 9.03729 12.9493C9.51683 12.4697 10.1546 12.2056 10.8328 12.2056C11.5109 12.2056 12.1486 12.4697 12.6281 12.9493C13.6181 13.9393 13.6181 15.55 12.6281 16.5401C12.1486 17.0197 11.5109 17.2838 10.8328 17.2838Z" fill="#388CB3"/>
<path d="M12.628 12.9492L9.03711 16.5401C9.51665 17.0196 10.1544 17.2838 10.8326 17.2838C11.5107 17.2838 12.1484 17.0196 12.628 16.5401C13.6178 15.5501 13.6178 13.9392 12.628 12.9492Z" fill="#265D77"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,15 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 6.47823V7.39644C0 7.51684 0.0975848 7.61442 0.217979 7.61442H8.12501V6.26025H0.217979C0.0975848 6.26025 0 6.35784 0 6.47823Z" fill="#80D261"/>
<path d="M14.2606 0.16662C10.1904 0.144022 6.83113 3.43254 6.77133 7.50241C6.75191 8.82462 7.07708 10.0693 7.66284 11.1524C7.89233 11.5767 7.81671 12.101 7.47563 12.4422L6.82859 13.0892L7.44761 14.3853L8.74368 15.0042L9.39072 14.3572C9.73184 14.0161 10.2562 13.9405 10.6805 14.17C11.7636 14.7558 13.0083 15.0809 14.3305 15.0615C18.4003 15.0017 21.6889 11.6424 21.6663 7.57219C21.6436 3.49229 18.3405 0.18926 14.2606 0.16662Z" fill="#D4F2F6"/>
<path d="M8.80176 7.61426C8.80176 10.6058 11.2269 13.0309 14.2184 13.0309C16.9807 13.0309 19.26 10.9633 19.5932 8.29135L17.6039 7.61426L16.2497 5.58301H14.8955L13.5413 9.64551H12.1872L10.833 6.93718L8.80176 7.61426Z" fill="#6BD9E7"/>
<path d="M17.6041 7.61443L16.2499 5.58318H14.8957L13.5416 9.64568H12.1874L11.5103 6.93734L8.97266 6.26026C9.57399 3.92402 11.6947 2.19775 14.2187 2.19775C16.9809 2.19775 19.2602 4.26536 19.5934 6.93734L17.6041 7.61443Z" fill="#6BD9E7"/>
<path d="M18.0722 7.77344C17.5411 10.2015 15.3802 12.0283 12.7805 12.0283C11.9447 12.0283 11.1532 11.8389 10.4463 11.5009C11.4217 12.4479 12.7523 13.031 14.2192 13.031C16.9814 13.031 19.2607 10.9634 19.5939 8.29136L18.0722 7.77344Z" fill="#02C7DD"/>
<path d="M18.1965 6.61169C18.1965 6.89107 18.1753 7.16563 18.1344 7.4338L19.5932 6.93728C19.3609 5.07382 18.1819 3.50429 16.5527 2.7251C17.5678 3.7114 18.1965 5.09096 18.1965 6.61169Z" fill="#02C7DD"/>
<path d="M8.31407 16.2938L8.74402 15.004L6.82893 13.0889L5.53908 13.5188C5.44276 13.5509 5.35525 13.605 5.28348 13.6768L0.319224 18.641C-0.106408 19.0666 -0.106408 19.7567 0.319224 20.1823L1.65054 21.5136C2.07617 21.9392 2.76625 21.9392 3.19184 21.5136L8.15609 16.5494C8.22791 16.4776 8.28199 16.3901 8.31407 16.2938Z" fill="#017297"/>
<path d="M10.833 7.83257V9.23307C10.833 9.83496 11.321 10.3229 11.9229 10.3229H13.8057C14.4076 10.3229 14.8955 9.83496 14.8955 9.23303V6.47836C14.8955 6.35796 14.9931 6.26038 15.1135 6.26038H16.0317C16.1521 6.26038 16.2497 6.35796 16.2497 6.47836V7.20178C16.2497 7.80371 16.7377 8.29167 17.3396 8.29167H19.5932C19.6209 8.06984 19.6351 7.84391 19.6351 7.61459C19.6351 7.38527 19.6209 7.15933 19.5932 6.9375H17.8219C17.7015 6.9375 17.6039 6.83992 17.6039 6.71952V5.9961C17.6039 5.39422 17.1159 4.90625 16.514 4.90625H14.6312C14.0293 4.90625 13.5413 5.39422 13.5413 5.99614V8.75082C13.5413 8.87121 13.4438 8.9688 13.3234 8.9688H12.4052C12.2848 8.9688 12.1872 8.87121 12.1872 8.75082V7.35031C12.1872 6.74839 11.6992 6.26042 11.0973 6.26042H8.97238C8.861 6.69324 8.80176 7.14698 8.80176 7.61459H10.615C10.7354 7.61459 10.833 7.71217 10.833 7.83257Z" fill="#80D261"/>
<path d="M19.5931 6.9375H18.1867C18.1588 7.40723 18.071 7.86117 17.9307 8.29167H19.5931C19.6208 8.06984 19.6351 7.8439 19.6351 7.61458C19.635 7.38526 19.6208 7.15933 19.5931 6.9375Z" fill="#68CA44"/>
<path d="M16.1203 11.3269L12.9528 16.8131C12.7011 17.2491 13.0158 17.794 13.5191 17.794H19.854C20.3574 17.794 20.672 17.2491 20.4203 16.8131L17.2529 11.3269C17.0012 10.891 16.372 10.891 16.1203 11.3269Z" fill="#FFC344"/>
<path d="M7.30793 15.8097C7.22427 15.8097 7.14056 15.7777 7.07675 15.7139L6.11922 14.7564C5.99151 14.6287 5.99151 14.4217 6.11922 14.294C6.2469 14.1663 6.45392 14.1663 6.58163 14.294L7.53915 15.2515C7.66687 15.3792 7.66687 15.5862 7.53915 15.7139C7.4753 15.7778 7.39159 15.8097 7.30793 15.8097Z" fill="#025F80"/>
<path d="M16.6873 15.4124C16.5067 15.4124 16.3604 15.266 16.3604 15.0854V13.7312C16.3604 13.5507 16.5067 13.4043 16.6873 13.4043C16.8679 13.4043 17.0142 13.5507 17.0142 13.7312V15.0854C17.0142 15.2659 16.8679 15.4124 16.6873 15.4124Z" fill="#017297"/>
<path d="M16.6863 16.7725C16.8669 16.7725 17.0133 16.6262 17.0133 16.4456C17.0133 16.265 16.8669 16.1187 16.6863 16.1187C16.5058 16.1187 16.3594 16.265 16.3594 16.4456C16.3594 16.6262 16.5058 16.7725 16.6863 16.7725Z" fill="#017297"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -0,0 +1,14 @@
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.9996 0.169434C15.5133 0.169434 19.9996 4.6557 19.9996 10.1694C19.9996 15.6832 15.5133 20.1694 9.9996 20.1694C4.48548 20.1694 0 15.6832 0 10.1694C0 4.6557 4.48548 0.169434 9.9996 0.169434Z" fill="#DCF5F8"/>
<path d="M10.0002 17.1381C13.8491 17.1381 16.9692 14.018 16.9692 10.1692C16.9692 6.32031 13.8491 3.2002 10.0002 3.2002C6.15136 3.2002 3.03125 6.32031 3.03125 10.1692C3.03125 14.018 6.15136 17.1381 10.0002 17.1381Z" fill="#A6E7F0"/>
<path d="M9.99894 12.9202C11.5182 12.9202 12.7498 11.6886 12.7498 10.1694C12.7498 8.65008 11.5182 7.41846 9.99894 7.41846C8.47967 7.41846 7.24805 8.65008 7.24805 10.1694C7.24805 11.6886 8.47967 12.9202 9.99894 12.9202Z" fill="#2ED1E2"/>
<path d="M9.99855 11.7261C10.8585 11.7261 11.5557 11.0289 11.5557 10.169C11.5557 9.30897 10.8585 8.61182 9.99855 8.61182C9.13856 8.61182 8.44141 9.30897 8.44141 10.169C8.44141 11.0289 9.13856 11.7261 9.99855 11.7261Z" fill="#02C7DD"/>
<path d="M6.36004 0.85498L7.84432 3.53186C5.72693 4.22108 4.05125 5.89676 3.36203 8.01415L0.685547 6.53027C1.70107 3.93894 3.76871 1.8709 6.36004 0.85498Z" fill="#A6E7F0"/>
<path d="M2.57406 9.29723C2.57406 5.89778 4.09359 2.85365 6.49005 0.805664C2.70227 2.23014 0 5.88999 0 10.1694C0 15.6831 4.48548 20.1694 9.9996 20.1694C10.3326 20.1694 10.6618 20.1527 10.9865 20.1207C6.15137 18.8944 2.57406 14.5137 2.57406 9.29723Z" fill="#A6E7F0"/>
<path d="M7.84362 3.53174L9.99881 7.41833C8.4819 7.41833 7.24792 8.65231 7.24792 10.1692L3.36133 8.01403C4.05054 5.89664 5.72623 4.22095 7.84362 3.53174Z" fill="#6BD9E7"/>
<path d="M6.38611 0.898474L6.36199 0.85498C3.77067 1.8709 1.70302 3.93894 0.6875 6.53027L2.6982 7.64503C3.09542 4.96823 4.44269 2.60194 6.38611 0.898474Z" fill="#6BD9E7"/>
<path d="M8.5957 3.6387C8.5965 3.63851 8.5973 3.63837 8.59809 3.63818C8.5973 3.63837 8.5965 3.63856 8.5957 3.6387Z" fill="#A6E7F0"/>
<path d="M14.2321 5.60636C14.7735 5.60636 15.2123 5.16752 15.2123 4.62618C15.2123 4.08484 14.7735 3.646 14.2321 3.646C13.6908 3.646 13.252 4.08484 13.252 4.62618C13.252 5.16752 13.6908 5.60636 14.2321 5.60636Z" fill="#EE6161"/>
<path d="M7.79097 17.7514C8.22781 17.7514 8.58193 17.3972 8.58193 16.9604C8.58193 16.5236 8.22781 16.1694 7.79097 16.1694C7.35413 16.1694 7 16.5236 7 16.9604C7 17.3972 7.35413 17.7514 7.79097 17.7514Z" fill="#97DA7B"/>
<path d="M15.5 11.0194C12.9318 11.0194 10.85 13.1013 10.85 15.6694C10.85 18.2376 12.9318 20.3194 15.5 20.3194C18.0682 20.3194 20.15 18.2376 20.15 15.6694C20.15 13.1013 18.0682 11.0194 15.5 11.0194ZM11.9594 15.6694C11.9594 14.5299 12.4977 13.5163 13.3344 12.8685L16.6926 19.0039C16.3201 19.1372 15.9187 19.2099 15.5 19.2099C13.5446 19.21 11.9594 17.6248 11.9594 15.6694ZM19.0404 15.6694C19.0404 16.8089 18.5021 17.8226 17.6655 18.4704L14.3073 12.335C14.6798 12.2017 15.0813 12.1289 15.5 12.1289C17.4554 12.1289 19.0404 13.714 19.0404 15.6694Z" fill="#FF5A73" stroke="#FF5A73" stroke-width="0.3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,23 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9922 20.0771H1.75781C0.788555 20.0771 0 19.2886 0 18.3193V4.17871C0 3.20945 0.788555 2.4209 1.75781 2.4209H11.9922C12.9614 2.4209 13.75 3.20945 13.75 4.17871V18.3193C13.75 19.2886 12.9614 20.0771 11.9922 20.0771Z" fill="#FF5E82"/>
<path d="M11.9922 2.4209H6.875V20.0771H11.9922C12.9614 20.0771 13.75 19.2886 13.75 18.3193V4.17871C13.75 3.20945 12.9614 2.4209 11.9922 2.4209Z" fill="#B7457D"/>
<path d="M1.75781 3.59277C1.43473 3.59277 1.17188 3.85563 1.17188 4.17871V18.3193C1.17188 18.6424 1.43473 18.9053 1.75781 18.9053H11.9922C12.3153 18.9053 12.5781 18.6424 12.5781 18.3193V4.17871C12.5781 3.85563 12.3153 3.59277 11.9922 3.59277H1.75781Z" fill="#F2FAFF"/>
<path d="M11.9922 3.59277H6.875V18.9053H11.9922C12.3153 18.9053 12.5781 18.6424 12.5781 18.3193V4.17871C12.5781 3.85563 12.3153 3.59277 11.9922 3.59277Z" fill="#C8EAFA"/>
<path d="M5.00834 7.97561L5.76592 7.21803C5.99475 6.9892 5.99475 6.61822 5.76592 6.38939C5.53713 6.16057 5.16611 6.16057 4.93728 6.38939L4.17971 7.14697L3.42213 6.38939C3.19334 6.16057 2.82232 6.16057 2.5935 6.38939C2.36467 6.61822 2.36467 6.9892 2.5935 7.21803L3.35107 7.97561L2.5935 8.73318C2.36467 8.96201 2.36467 9.33299 2.5935 9.56182C2.70791 9.67623 2.85787 9.73342 3.00783 9.73342C3.15779 9.73342 3.30775 9.67623 3.42213 9.56178L4.17971 8.80424L4.93728 9.56182C5.05166 9.67623 5.20162 9.73342 5.35158 9.73342C5.50154 9.73342 5.6515 9.67623 5.76588 9.56178C5.99471 9.33295 5.99471 8.96197 5.76588 8.73314L5.00834 7.97561Z" fill="#5E54AC"/>
<path d="M5.00834 11.9756L5.76592 11.218C5.99475 10.9892 5.99475 10.6182 5.76592 10.3894C5.53713 10.1606 5.16611 10.1606 4.93728 10.3894L4.17971 11.147L3.42213 10.3894C3.19334 10.1606 2.82232 10.1606 2.5935 10.3894C2.36467 10.6182 2.36467 10.9892 2.5935 11.218L3.35107 11.9756L2.5935 12.7332C2.36467 12.962 2.36467 13.333 2.5935 13.5618C2.70791 13.6762 2.85787 13.7334 3.00783 13.7334C3.15779 13.7334 3.30775 13.6762 3.42213 13.5618L4.17971 12.8042L4.93728 13.5618C5.05166 13.6762 5.20162 13.7334 5.35158 13.7334C5.50154 13.7334 5.6515 13.6762 5.76588 13.5618C5.99471 13.3329 5.99471 12.962 5.76588 12.7331L5.00834 11.9756Z" fill="#5E54AC"/>
<path d="M5.00834 15.9756L5.76592 15.218C5.99475 14.9892 5.99475 14.6182 5.76592 14.3894C5.53713 14.1606 5.16611 14.1606 4.93728 14.3894L4.17971 15.147L3.42213 14.3894C3.19334 14.1606 2.82232 14.1606 2.5935 14.3894C2.36467 14.6182 2.36467 14.9892 2.5935 15.218L3.35107 15.9756L2.5935 16.7332C2.36467 16.962 2.36467 17.333 2.5935 17.5618C2.70791 17.6762 2.85787 17.7334 3.00783 17.7334C3.15779 17.7334 3.30775 17.6762 3.42213 17.5618L4.17971 16.8042L4.93728 17.5618C5.05166 17.6762 5.20162 17.7334 5.35158 17.7334C5.50154 17.7334 5.6515 17.6762 5.76588 17.5618C5.99471 17.3329 5.99471 16.962 5.76588 16.7331L5.00834 15.9756Z" fill="#5E54AC"/>
<path d="M19.4141 20.0771H15.5078C15.1842 20.0771 14.9219 19.8148 14.9219 19.4912V15.585C14.9219 15.2614 15.1842 14.999 15.5078 14.999H19.4141C19.7377 14.999 20 15.2614 20 15.585V19.4912C20 19.8148 19.7377 20.0771 19.4141 20.0771Z" fill="#F2FAFF"/>
<path d="M19.4141 14.999H17.4609V20.0771H19.4141C19.7377 20.0771 20 19.8148 20 19.4912V15.585C20 15.2614 19.7377 14.999 19.4141 14.999Z" fill="#C8EAFA"/>
<path d="M18.2422 12.6553H16.6797C15.7104 12.6553 14.9219 13.4438 14.9219 14.4131V15.585H20V14.4131C20 13.4438 19.2114 12.6553 18.2422 12.6553Z" fill="#A4E9FF"/>
<path d="M20 14.4131C20 13.4438 19.2114 12.6553 18.2422 12.6553H17.4609V15.585H20V14.4131Z" fill="#91BBFF"/>
<path d="M10 4.76465H3.75C3.42641 4.76465 3.16406 4.5023 3.16406 4.17871V3.39746C3.16406 2.46688 3.8909 1.70289 4.80672 1.6434C5.06109 0.740625 5.89199 0.0771484 6.875 0.0771484C7.85801 0.0771484 8.68891 0.740625 8.94328 1.6434C9.8591 1.70289 10.5859 2.46688 10.5859 3.39746V4.17871C10.5859 4.5023 10.3236 4.76465 10 4.76465Z" fill="#FFE67A"/>
<path d="M8.94328 1.6434C8.68891 0.740625 7.85801 0.0771484 6.875 0.0771484V4.76465H10C10.3236 4.76465 10.5859 4.5023 10.5859 4.17871V3.39746C10.5859 2.46688 9.8591 1.70289 8.94328 1.6434Z" fill="#FFC336"/>
<path d="M7.72045 15.5587C7.52627 15.3645 7.49299 15.0614 7.64045 14.8297L9.57393 11.7914C9.69295 11.6043 9.90991 11.4972 10.1383 11.5241C10.2743 11.5401 10.399 11.6081 10.4959 11.7049L11.5741 12.7831C11.671 12.88 11.7389 13.0047 11.7549 13.1408C11.7818 13.3691 11.6748 13.5861 11.4876 13.7051L8.44928 15.6386C8.21764 15.7861 7.91463 15.7528 7.72045 15.5587Z" fill="#FFD6AA"/>
<path d="M11.5724 12.7832L11.0333 12.2441L7.71875 15.5587C7.91293 15.7529 8.21598 15.7862 8.44766 15.6387L11.486 13.7052C11.6731 13.5862 11.7802 13.3693 11.7533 13.1409C11.7373 13.0048 11.6693 12.8801 11.5724 12.7832Z" fill="#FAC68F"/>
<path d="M18.4926 5.89121L17.3878 4.78637C17.1589 4.55754 16.7879 4.55754 16.5591 4.78637L15.3162 6.02934C15.0873 6.25816 15.0873 6.62914 15.3162 6.85797L16.421 7.96281C16.6498 8.19164 17.0208 8.19164 17.2496 7.96281L18.4926 6.71984C18.7214 6.49105 18.7214 6.12004 18.4926 5.89121Z" fill="#F2FAFF"/>
<path d="M17.9407 5.33887L15.8691 7.41047L16.4216 7.96289C16.6504 8.19172 17.0214 8.19172 17.2502 7.96289L18.4932 6.71992C18.722 6.49109 18.722 6.12012 18.4932 5.89129L17.9407 5.33887Z" fill="#C8EAFA"/>
<path d="M19.5969 3.68168C19.063 3.14777 18.1973 3.14777 17.6634 3.68168L16.5586 4.78652L18.4921 6.72L19.5969 5.61516C20.1308 5.08125 20.1308 4.21559 19.5969 3.68168Z" fill="#FF5E82"/>
<path d="M19.5989 3.68164L17.5273 5.75324L18.4941 6.72L19.5989 5.61516C20.1329 5.08121 20.1329 4.21555 19.5989 3.68164Z" fill="#B7457D"/>
<path d="M9.65234 11.6919L15.3139 6.03037L17.2471 7.96357L11.5855 13.6251L9.65234 11.6919Z" fill="#BC8173"/>
<path d="M10.6191 12.6587L16.2807 6.99717L17.2473 7.96377L11.5857 13.6253L10.6191 12.6587Z" fill="#9D5E4A"/>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,23 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9922 20.0464H1.75781C0.788555 20.0464 0 19.2578 0 18.2886V4.14795C0 3.17869 0.788555 2.39014 1.75781 2.39014H11.9922C12.9614 2.39014 13.75 3.17869 13.75 4.14795V18.2886C13.75 19.2578 12.9614 20.0464 11.9922 20.0464Z" fill="#FF5E82"/>
<path d="M11.9922 2.39014H6.875V20.0464H11.9922C12.9614 20.0464 13.75 19.2578 13.75 18.2886V4.14795C13.75 3.17869 12.9614 2.39014 11.9922 2.39014Z" fill="#B7457D"/>
<path d="M1.75781 3.56201C1.43473 3.56201 1.17188 3.82486 1.17188 4.14795V18.2886C1.17188 18.6117 1.43473 18.8745 1.75781 18.8745H11.9922C12.3153 18.8745 12.5781 18.6117 12.5781 18.2886V4.14795C12.5781 3.82486 12.3153 3.56201 11.9922 3.56201H1.75781Z" fill="#F2FAFF"/>
<path d="M11.9922 3.56201H6.875V18.8745H11.9922C12.3153 18.8745 12.5781 18.6117 12.5781 18.2886V4.14795C12.5781 3.82486 12.3153 3.56201 11.9922 3.56201Z" fill="#C8EAFA"/>
<path d="M4.01889 9.36547C3.86893 9.36547 3.71896 9.30828 3.60459 9.19383L2.49975 8.08898C2.27092 7.86016 2.27092 7.48918 2.49975 7.26035C2.72854 7.03152 3.09955 7.03152 3.32838 7.26035L4.01893 7.9509L5.81436 6.15551C6.04318 5.92668 6.4142 5.92668 6.64299 6.15551C6.87182 6.38434 6.87182 6.75531 6.64299 6.98414L4.43326 9.19383C4.31881 9.30828 4.16881 9.36547 4.01889 9.36547Z" fill="#5E54AC"/>
<path d="M4.01889 13.2107C3.86893 13.2107 3.71896 13.1535 3.60459 13.039L2.49975 11.9342C2.27092 11.7054 2.27092 11.3344 2.49975 11.1056C2.72854 10.8767 3.09955 10.8767 3.32838 11.1056L4.01893 11.7961L5.81436 10.0007C6.04318 9.77189 6.4142 9.77189 6.64299 10.0007C6.87182 10.2296 6.87182 10.6005 6.64299 10.8294L4.43326 13.039C4.31881 13.1535 4.16881 13.2107 4.01889 13.2107Z" fill="#5E54AC"/>
<path d="M4.01889 17.2107C3.86893 17.2107 3.71896 17.1535 3.60459 17.039L2.49975 15.9342C2.27092 15.7054 2.27092 15.3344 2.49975 15.1056C2.72854 14.8767 3.09955 14.8767 3.32838 15.1056L4.01893 15.7961L5.81436 14.0007C6.04318 13.7719 6.4142 13.7719 6.64299 14.0007C6.87182 14.2296 6.87182 14.6005 6.64299 14.8294L4.43326 17.039C4.31881 17.1535 4.16881 17.2107 4.01889 17.2107Z" fill="#5E54AC"/>
<path d="M19.4141 20.0464H15.5078C15.1842 20.0464 14.9219 19.784 14.9219 19.4604V15.5542C14.9219 15.2306 15.1842 14.9683 15.5078 14.9683H19.4141C19.7377 14.9683 20 15.2306 20 15.5542V19.4604C20 19.784 19.7377 20.0464 19.4141 20.0464Z" fill="#F2FAFF"/>
<path d="M19.4141 14.9683H17.4609V20.0464H19.4141C19.7377 20.0464 20 19.784 20 19.4604V15.5542C20 15.2306 19.7377 14.9683 19.4141 14.9683Z" fill="#C8EAFA"/>
<path d="M18.2422 12.6245H16.6797C15.7104 12.6245 14.9219 13.4131 14.9219 14.3823V15.5542H20V14.3823C20 13.4131 19.2114 12.6245 18.2422 12.6245Z" fill="#A4E9FF"/>
<path d="M20 14.3823C20 13.4131 19.2114 12.6245 18.2422 12.6245H17.4609V15.5542H20V14.3823Z" fill="#91BBFF"/>
<path d="M10 4.73389H3.75C3.42641 4.73389 3.16406 4.47154 3.16406 4.14795V3.3667C3.16406 2.43611 3.8909 1.67213 4.80672 1.61264C5.06109 0.709863 5.89199 0.0463867 6.875 0.0463867C7.85801 0.0463867 8.68891 0.709863 8.94328 1.61264C9.8591 1.67213 10.5859 2.43611 10.5859 3.3667V4.14795C10.5859 4.47154 10.3236 4.73389 10 4.73389Z" fill="#FFE67A"/>
<path d="M8.94328 1.61264C8.68891 0.709863 7.85801 0.0463867 6.875 0.0463867V4.73389H10C10.3236 4.73389 10.5859 4.47154 10.5859 4.14795V3.3667C10.5859 2.43611 9.8591 1.67213 8.94328 1.61264Z" fill="#FFC336"/>
<path d="M7.72045 15.5279C7.52627 15.3337 7.49299 15.0307 7.64045 14.799L9.57393 11.7606C9.69295 11.5736 9.90991 11.4665 10.1383 11.4934C10.2743 11.5094 10.399 11.5773 10.4959 11.6742L11.5741 12.7524C11.671 12.8493 11.7389 12.9739 11.7549 13.11C11.7818 13.3384 11.6748 13.5553 11.4876 13.6743L8.44928 15.6078C8.21764 15.7554 7.91463 15.7221 7.72045 15.5279Z" fill="#FFD6AA"/>
<path d="M11.5724 12.7525L11.0333 12.2134L7.71875 15.5279C7.91293 15.7221 8.21598 15.7554 8.44766 15.6079L11.486 13.6745C11.6731 13.5554 11.7802 13.3385 11.7533 13.1101C11.7373 12.974 11.6693 12.8494 11.5724 12.7525Z" fill="#FAC68F"/>
<path d="M18.4926 5.86045L17.3878 4.75561C17.1589 4.52678 16.7879 4.52678 16.5591 4.75561L15.3162 5.99857C15.0873 6.2274 15.0873 6.59838 15.3162 6.82721L16.421 7.93205C16.6498 8.16088 17.0208 8.16088 17.2496 7.93205L18.4926 6.68908C18.7214 6.46029 18.7214 6.08928 18.4926 5.86045Z" fill="#F2FAFF"/>
<path d="M17.9407 5.30811L15.8691 7.37971L16.4216 7.93213C16.6504 8.16096 17.0214 8.16096 17.2502 7.93213L18.4932 6.68916C18.722 6.46033 18.722 6.08936 18.4932 5.86053L17.9407 5.30811Z" fill="#C8EAFA"/>
<path d="M19.5969 3.65092C19.063 3.11701 18.1973 3.11701 17.6634 3.65092L16.5586 4.75576L18.4921 6.68924L19.5969 5.58439C20.1308 5.05049 20.1308 4.18482 19.5969 3.65092Z" fill="#FF5E82"/>
<path d="M19.5989 3.65088L17.5273 5.72248L18.4941 6.68924L19.5989 5.58439C20.1329 5.05045 20.1329 4.18479 19.5989 3.65088Z" fill="#B7457D"/>
<path d="M9.65234 11.6611L15.3139 5.99961L17.2471 7.93281L11.5855 13.5943L9.65234 11.6611Z" fill="#BC8173"/>
<path d="M10.6191 12.6279L16.2807 6.96641L17.2473 7.93301L11.5857 13.5945L10.6191 12.6279Z" fill="#9D5E4A"/>
</svg>

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,41 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9922 20.1079H1.75781C0.788555 20.1079 0 19.3194 0 18.3501V4.20947C0 3.24021 0.788555 2.45166 1.75781 2.45166H11.9922C12.9614 2.45166 13.75 3.24021 13.75 4.20947V18.3501C13.75 19.3194 12.9614 20.1079 11.9922 20.1079Z" fill="#FF5E82"/>
<path d="M11.9922 2.45166H6.875V20.1079H11.9922C12.9614 20.1079 13.75 19.3194 13.75 18.3501V4.20947C13.75 3.24021 12.9614 2.45166 11.9922 2.45166Z" fill="#B7457D"/>
<path d="M1.75781 3.62354C1.43473 3.62354 1.17188 3.88639 1.17188 4.20947V18.3501C1.17188 18.6732 1.43473 18.936 1.75781 18.936H11.9922C12.3153 18.936 12.5781 18.6732 12.5781 18.3501V4.20947C12.5781 3.88639 12.3153 3.62354 11.9922 3.62354H1.75781Z" fill="#F2FAFF"/>
<path d="M11.9922 3.62354H6.875V18.936H11.9922C12.3153 18.936 12.5781 18.6732 12.5781 18.3501V4.20947C12.5781 3.88639 12.3153 3.62354 11.9922 3.62354Z" fill="#C8EAFA"/>
<path d="M12.4999 20.1083C14.9852 20.1083 16.9999 18.0936 16.9999 15.6083C16.9999 13.1231 14.9852 11.1084 12.4999 11.1084C10.0147 11.1084 8 13.1231 8 15.6083C8 18.0936 10.0147 20.1083 12.4999 20.1083Z" fill="#F07281"/>
<path d="M12.69 20.1039C12.6271 20.1065 12.5636 20.1078 12.5 20.1078C10.0148 20.1078 8 18.0931 8 15.6079C8 13.1227 10.0148 11.1079 12.5 11.1079C12.5636 11.1079 12.6271 11.1092 12.69 11.1119C10.293 11.2115 8.38013 13.1863 8.38013 15.6079C8.38013 18.0294 10.293 20.0043 12.69 20.1039Z" fill="#EB5569"/>
<path d="M12.4994 19.5174C14.6586 19.5174 16.409 17.767 16.409 15.6078C16.409 13.4486 14.6586 11.6982 12.4994 11.6982C10.3402 11.6982 8.58984 13.4486 8.58984 15.6078C8.58984 17.767 10.3402 19.5174 12.4994 19.5174Z" fill="#EAF6FF"/>
<path d="M12.7846 19.5072C12.6905 19.514 12.5952 19.5175 12.4994 19.5175C10.3402 19.5175 8.58984 17.7671 8.58984 15.6078C8.58984 13.4486 10.3402 11.6982 12.4995 11.6982C12.5953 11.6982 12.6905 11.7017 12.7846 11.7085C10.7584 11.8545 9.16007 13.5444 9.16007 15.6078C9.16007 17.6713 10.7584 19.3612 12.7846 19.5072Z" fill="#D8ECFE"/>
<path d="M12.2539 13.3321L12.4284 13.2198C12.471 13.1924 12.5257 13.1924 12.5682 13.2198L12.7428 13.3321C12.7744 13.3525 12.7935 13.3876 12.7935 13.4252V15.6605H12.2031V13.4252C12.2031 13.3876 12.2222 13.3525 12.2539 13.3321Z" fill="#5680A6"/>
<path d="M12.6884 13.2972L12.5833 13.3648V15.6605H12.2031V13.4353C12.2031 13.3914 12.2254 13.3504 12.2624 13.3267L12.4284 13.2198C12.471 13.1924 12.5256 13.1924 12.5682 13.2198L12.6884 13.2972Z" fill="#497090"/>
<path d="M14.3445 15.6776L14.2376 15.8436C14.2138 15.8806 14.1729 15.9029 14.129 15.9029H12.5V15.3125H14.129C14.1729 15.3125 14.2138 15.3348 14.2376 15.3717L14.3445 15.5378C14.3719 15.5803 14.3719 15.635 14.3445 15.6776Z" fill="#5680A6"/>
<path d="M13.2936 15.6077C13.2936 15.712 13.2734 15.8116 13.2365 15.9029H12.5V15.3125H13.2365C13.2734 15.4037 13.2936 15.5033 13.2936 15.6077Z" fill="#497090"/>
<path d="M12.4993 16.0211C12.7277 16.0211 12.9127 15.8361 12.9127 15.6077C12.9127 15.3794 12.7277 15.1943 12.4993 15.1943C12.271 15.1943 12.0859 15.3794 12.0859 15.6077C12.0859 15.8361 12.271 16.0211 12.4993 16.0211Z" fill="#F07281"/>
<path d="M12.6894 15.9748C12.6326 16.0044 12.568 16.0211 12.4993 16.0211C12.2711 16.0211 12.0859 15.836 12.0859 15.6077C12.0859 15.3795 12.2711 15.1943 12.4993 15.1943C12.568 15.1943 12.6326 15.2111 12.6894 15.2407C12.5567 15.3093 12.4661 15.4481 12.4661 15.6078C12.4661 15.7674 12.5567 15.9061 12.6894 15.9748Z" fill="#EB5569"/>
<path d="M12.5019 12.7788C12.4232 12.7788 12.3594 12.715 12.3594 12.6363V12.268C12.3594 12.1893 12.4232 12.1255 12.5019 12.1255C12.5807 12.1255 12.6445 12.1893 12.6445 12.268V12.6363C12.6445 12.715 12.5807 12.7788 12.5019 12.7788Z" fill="#88B4F5"/>
<path d="M12.5019 19.0894C12.4232 19.0894 12.3594 19.0255 12.3594 18.9468V18.5786C12.3594 18.4999 12.4232 18.436 12.5019 18.436C12.5807 18.436 12.6445 18.4999 12.6445 18.5786V18.9468C12.6445 19.0255 12.5807 19.0894 12.5019 19.0894Z" fill="#88B4F5"/>
<path d="M9.52445 15.7504H9.15622C9.07749 15.7504 9.01367 15.6866 9.01367 15.6079C9.01367 15.5292 9.07749 15.4653 9.15622 15.4653H9.52445C9.60318 15.4653 9.667 15.5292 9.667 15.6079C9.667 15.6866 9.60318 15.7504 9.52445 15.7504Z" fill="#88B4F5"/>
<path d="M15.835 15.7504H15.4668C15.388 15.7504 15.3242 15.6866 15.3242 15.6079C15.3242 15.5292 15.388 15.4653 15.4668 15.4653H15.835C15.9137 15.4653 15.9775 15.5292 15.9775 15.6079C15.9775 15.6866 15.9137 15.7504 15.835 15.7504Z" fill="#88B4F5"/>
<path d="M9.60755 17.4199C9.55829 17.4199 9.51037 17.3944 9.48397 17.3486C9.4446 17.2805 9.46796 17.1933 9.53615 17.1539L9.85505 16.9698C9.92318 16.9305 10.0104 16.9538 10.0498 17.022C10.0891 17.0901 10.0658 17.1773 9.99759 17.2167L9.6787 17.4008C9.65626 17.4138 9.63174 17.4199 9.60755 17.4199Z" fill="#88B4F5"/>
<path d="M15.0724 14.2647C15.0231 14.2647 14.9752 14.2391 14.9488 14.1934C14.9094 14.1252 14.9328 14.038 15.001 13.9986L15.3199 13.8145C15.388 13.7752 15.4752 13.7985 15.5146 13.8667C15.554 13.9349 15.5306 14.0221 15.4624 14.0614L15.1435 14.2456C15.1211 14.2585 15.0966 14.2647 15.0724 14.2647Z" fill="#88B4F5"/>
<path d="M9.92619 14.2647C9.902 14.2647 9.8775 14.2585 9.85505 14.2456L9.53615 14.0614C9.46796 14.0221 9.4446 13.9349 9.48397 13.8667C9.52333 13.7985 9.6105 13.7752 9.6787 13.8145L9.99759 13.9987C10.0658 14.038 10.0891 14.1252 10.0498 14.1934C10.0234 14.2391 9.97545 14.2647 9.92619 14.2647Z" fill="#88B4F5"/>
<path d="M10.8319 18.6422C10.8077 18.6422 10.7832 18.636 10.7608 18.623C10.6926 18.5836 10.6692 18.4965 10.7086 18.4283L10.8927 18.1094C10.9321 18.0412 11.0192 18.0178 11.0874 18.0572C11.1556 18.0966 11.179 18.1838 11.1396 18.2519L10.9555 18.5708C10.9291 18.6166 10.8812 18.6422 10.8319 18.6422Z" fill="#88B4F5"/>
<path d="M13.9862 13.1773C13.962 13.1773 13.9375 13.1711 13.9151 13.1582C13.8469 13.1188 13.8235 13.0316 13.8629 12.9634L14.047 12.6445C14.0863 12.5764 14.1735 12.553 14.2417 12.5924C14.3099 12.6317 14.3333 12.7189 14.2939 12.7871L14.1098 13.106C14.0834 13.1517 14.0355 13.1773 13.9862 13.1773Z" fill="#88B4F5"/>
<path d="M11.0163 13.1778C10.967 13.1778 10.9191 13.1522 10.8927 13.1065L10.7086 12.7876C10.6692 12.7194 10.6926 12.6322 10.7608 12.5929C10.8289 12.5535 10.9161 12.5768 10.9555 12.645L11.1396 12.9639C11.179 13.0321 11.1556 13.1193 11.0874 13.1587C11.065 13.1716 11.0405 13.1778 11.0163 13.1778Z" fill="#88B4F5"/>
<path d="M15.3252 15.6128C15.3252 15.534 15.389 15.4702 15.4677 15.4702L15.836 15.4702C15.9147 15.4702 15.9785 15.534 15.9785 15.6128C15.9785 15.6915 15.9147 15.7553 15.836 15.7553L15.4677 15.7553C15.389 15.7553 15.3252 15.6915 15.3252 15.6128Z" fill="#88B4F5"/>
<path d="M9.01464 15.6128C9.01464 15.534 9.07846 15.4702 9.15719 15.4702L9.52542 15.4702C9.60415 15.4702 9.66797 15.534 9.66797 15.6128C9.66797 15.6915 9.60415 15.7553 9.52542 15.7553L9.15719 15.7553C9.07848 15.7553 9.01464 15.6915 9.01464 15.6128Z" fill="#88B4F5"/>
<path d="M12.3536 12.6363L12.3536 12.268C12.3536 12.1893 12.4174 12.1255 12.4961 12.1255C12.5749 12.1255 12.6387 12.1893 12.6387 12.268L12.6387 12.6363C12.6387 12.715 12.5749 12.7788 12.4961 12.7788C12.4174 12.7788 12.3536 12.715 12.3536 12.6363Z" fill="#88B4F5"/>
<path d="M12.3536 18.9468L12.3536 18.5786C12.3536 18.4998 12.4174 18.436 12.4961 18.436C12.5749 18.436 12.6387 18.4999 12.6387 18.5786L12.6387 18.9468C12.6387 19.0256 12.5749 19.0894 12.4961 19.0894C12.4174 19.0894 12.3536 19.0255 12.3536 18.9468Z" fill="#88B4F5"/>
<path d="M10.6831 12.7184C10.6831 12.6691 10.7087 12.6212 10.7544 12.5948C10.8226 12.5554 10.9098 12.5788 10.9491 12.647L11.1332 12.9659C11.1726 13.034 11.1492 13.1212 11.0811 13.1606C11.0129 13.2 10.9257 13.1766 10.8863 13.1084L10.7022 12.7895C10.6892 12.7671 10.6831 12.7426 10.6831 12.7184Z" fill="#88B4F5"/>
<path d="M13.8393 18.1832C13.8393 18.134 13.8649 18.0861 13.9106 18.0597C13.9788 18.0203 14.066 18.0437 14.1054 18.1118L14.2895 18.4307C14.3288 18.4989 14.3055 18.5861 14.2373 18.6255C14.1691 18.6648 14.0819 18.6414 14.0426 18.5733L13.8585 18.2544C13.8455 18.2319 13.8393 18.2074 13.8393 18.1832Z" fill="#88B4F5"/>
<path d="M13.8393 13.037C13.8393 13.0128 13.8455 12.9883 13.8584 12.9659L14.0426 12.647C14.0819 12.5788 14.1691 12.5554 14.2373 12.5948C14.3055 12.6342 14.3288 12.7213 14.2895 12.7895L14.1053 13.1084C14.066 13.1766 13.9788 13.2 13.9106 13.1606C13.8649 13.1342 13.8393 13.0863 13.8393 13.037Z" fill="#88B4F5"/>
<path d="M9.46038 13.9437C9.46038 13.9195 9.46655 13.895 9.47952 13.8726C9.51889 13.8044 9.60607 13.781 9.67424 13.8204L9.99314 14.0045C10.0613 14.0439 10.0847 14.1311 10.0453 14.1992C10.0059 14.2674 9.91877 14.2908 9.85059 14.2514L9.53169 14.0673C9.48596 14.0409 9.46038 13.993 9.46038 13.9437Z" fill="#88B4F5"/>
<path d="M14.9252 17.099C14.9252 17.0748 14.9314 17.0503 14.9444 17.0278C14.9837 16.9597 15.0709 16.9363 15.1391 16.9757L15.458 17.1598C15.5262 17.1991 15.5495 17.2863 15.5102 17.3545C15.4708 17.4227 15.3836 17.4461 15.3154 17.4067L14.9965 17.2226C14.9508 17.1962 14.9252 17.1483 14.9252 17.099Z" fill="#88B4F5"/>
<path d="M14.9252 14.1281C14.9252 14.0788 14.9508 14.0309 14.9965 14.0045L15.3154 13.8204C15.3836 13.781 15.4708 13.8044 15.5102 13.8726C15.5495 13.9407 15.5262 14.0279 15.458 14.0673L15.1391 14.2514C15.0709 14.2908 14.9837 14.2674 14.9444 14.1992C14.9314 14.1768 14.9252 14.1523 14.9252 14.1281Z" fill="#88B4F5"/>
<path d="M4.01889 9.27221C3.86893 9.27221 3.71896 9.21502 3.60459 9.10057L2.49975 7.99572C2.27092 7.76689 2.27092 7.39592 2.49975 7.16709C2.72854 6.93826 3.09955 6.93826 3.32838 7.16709L4.01893 7.85764L5.81436 6.06225C6.04318 5.83342 6.4142 5.83342 6.64299 6.06225C6.87182 6.29107 6.87182 6.66205 6.64299 6.89088L4.43326 9.10057C4.31881 9.21498 4.16881 9.27221 4.01889 9.27221Z" fill="#5E54AC"/>
<path d="M5.00834 12.0069L5.76592 11.2493C5.99475 11.0204 5.99475 10.6495 5.76592 10.4206C5.53713 10.1918 5.16611 10.1918 4.93728 10.4206L4.17971 11.1782L3.42213 10.4206C3.19334 10.1918 2.82232 10.1918 2.5935 10.4206C2.36467 10.6495 2.36467 11.0204 2.5935 11.2493L3.35107 12.0069L2.5935 12.7644C2.36467 12.9933 2.36467 13.3642 2.5935 13.5931C2.70791 13.7075 2.85787 13.7647 3.00783 13.7647C3.15779 13.7647 3.30775 13.7075 3.42213 13.593L4.17971 12.8355L4.93728 13.5931C5.05166 13.7075 5.20162 13.7647 5.35158 13.7647C5.50154 13.7647 5.6515 13.7075 5.76588 13.593C5.99471 13.3642 5.99471 12.9932 5.76588 12.7644L5.00834 12.0069Z" fill="#5E54AC"/>
<path d="M5.00834 16.0069L5.76592 15.2493C5.99475 15.0204 5.99475 14.6495 5.76592 14.4206C5.53713 14.1918 5.16611 14.1918 4.93728 14.4206L4.17971 15.1782L3.42213 14.4206C3.19334 14.1918 2.82232 14.1918 2.5935 14.4206C2.36467 14.6495 2.36467 15.0204 2.5935 15.2493L3.35107 16.0069L2.5935 16.7644C2.36467 16.9933 2.36467 17.3642 2.5935 17.5931C2.70791 17.7075 2.85787 17.7647 3.00783 17.7647C3.15779 17.7647 3.30775 17.7075 3.42213 17.593L4.17971 16.8355L4.93728 17.5931C5.05166 17.7075 5.20162 17.7647 5.35158 17.7647C5.50154 17.7647 5.6515 17.7075 5.76588 17.593C5.99471 17.3642 5.99471 16.9932 5.76588 16.7644L5.00834 16.0069Z" fill="#5E54AC"/>
<path d="M10 4.79541H3.75C3.42641 4.79541 3.16406 4.53307 3.16406 4.20947V3.42822C3.16406 2.49764 3.8909 1.73365 4.80672 1.67416C5.06109 0.771387 5.89199 0.10791 6.875 0.10791C7.85801 0.10791 8.68891 0.771387 8.94328 1.67416C9.8591 1.73365 10.5859 2.49764 10.5859 3.42822V4.20947C10.5859 4.53307 10.3236 4.79541 10 4.79541Z" fill="#FFE67A"/>
<path d="M8.94328 1.67416C8.68891 0.771387 7.85801 0.10791 6.875 0.10791V4.79541H10C10.3236 4.79541 10.5859 4.53307 10.5859 4.20947V3.42822C10.5859 2.49764 9.8591 1.73365 8.94328 1.67416Z" fill="#FFC336"/>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.72751C16.7022 7.47106 16.444 7.24286 16.1963 7.34188L15.166 7.75403C16.1394 3.617 13.6255 1.13192 12.3309 0.160156L11.9303 0.68629C12.295 4.20985 10.8697 6.77552 10.8697 6.77552L9.6861 6.06286C7.63047 8.86439 7.26813 11.637 7.26813 11.637L6.22457 10.9497L5.84278 15.599L4.19531 17.1113C4.19531 17.1113 9.12215 19.2668 13.862 14.8542C14.0281 14.6996 13.9924 14.4272 13.7895 14.3258L12.2619 13.562C13.7729 12.6635 15.8697 10.3879 16.629 7.72751Z" fill="#1479FF"/>
<path d="M6.94955 12.0658C7.20748 12.3235 7.64893 12.1695 7.69061 11.8073L7.74029 11.3759C7.74315 11.3515 8.03272 8.98838 9.75631 6.48396L10.3884 7.11599C10.5932 7.32076 10.9371 7.27255 11.0777 7.01939L11.2124 6.77685C11.2795 6.65646 12.7674 3.91306 12.3306 0.160279C12.2926 0.131763 12.2554 0.104185 12.2196 0.0782866C11.9917 -0.0867917 11.68 0.0943414 11.709 0.374303C12.0737 3.89786 10.6483 6.46353 10.6483 6.46353L9.94327 5.75849C9.80448 5.6197 9.57174 5.63474 9.45561 5.79298C7.40002 8.59451 7.099 11.3035 7.099 11.3035L6.50061 10.7051C6.32693 10.5315 6.03064 10.6084 5.96029 10.8437C5.23283 13.2767 5.71564 15.7659 5.71564 15.7659L5.71514 15.7664L6.34936 15.6438C6.34525 15.6222 5.97377 13.6265 6.44041 11.5575L6.94955 12.0658Z" fill="#D5EAFF"/>
<path d="M13.2564 6.00624C13.3141 5.83764 13.224 5.65428 13.0554 5.59659C12.8865 5.5394 12.7031 5.62905 12.6457 5.7976C11.8359 8.16772 10.4981 10.2792 8.9927 12.0909C8.98586 10.3632 9.52113 9.3046 9.5316 9.28437C9.61414 9.12683 9.55367 8.93194 9.39641 8.8489C9.23793 8.76601 9.04348 8.82636 8.96031 8.98394C8.92824 9.04499 8.19051 10.4827 8.37992 12.7999C4.65574 16.9746 0.236005 19.3733 0.170849 19.408C0.0136223 19.492 -0.0459481 19.6873 0.0381926 19.8446C0.0961615 19.9534 0.207724 20.0154 0.323037 20.0154C0.374404 20.0154 0.426084 20.0032 0.474599 19.9774C0.530303 19.9477 3.69996 18.2289 6.9675 15.1631C7.06207 15.1709 7.47437 15.2031 7.91512 15.2031C8.76023 15.2031 10.083 15.1232 11.1436 14.6989C11.3091 14.6326 11.3897 14.4449 11.3236 14.2793C11.2574 14.1139 11.068 14.034 10.9041 14.0996C9.80613 14.5389 8.34164 14.5695 7.6 14.552C9.86817 12.3043 12.09 9.41976 13.2564 6.00624Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.75827C16.7022 7.50183 16.444 7.27362 16.1963 7.37265L15.166 7.78479C16.1394 3.64776 13.6255 1.16268 12.3309 0.190918L11.9303 0.717051C12.295 4.24061 10.8697 6.80628 10.8697 6.80628L9.6861 6.09362C7.63047 8.89515 7.26813 11.6677 7.26813 11.6677L6.22457 10.9805L5.84278 15.6298L4.19531 17.1421C4.19531 17.1421 9.12215 19.2976 13.862 14.8849C14.0281 14.7303 13.9924 14.458 13.7895 14.3565L12.2619 13.5927C13.7729 12.6943 15.8697 10.4187 16.629 7.75827Z" fill="#3C90FF"/>
<path d="M6.94955 12.0966C7.20748 12.3542 7.64893 12.2002 7.69061 11.838L7.74029 11.4067C7.74315 11.3823 8.03272 9.01914 9.75631 6.51472L10.3884 7.14675C10.5932 7.35152 10.9371 7.30332 11.0777 7.05015L11.2124 6.80761C11.2795 6.68722 12.7674 3.94382 12.3306 0.191041C12.2926 0.162525 12.2554 0.134947 12.2196 0.109048C11.9917 -0.05603 11.68 0.125103 11.709 0.405064C12.0737 3.92862 10.6483 6.49429 10.6483 6.49429L9.94327 5.78925C9.80448 5.65046 9.57174 5.6655 9.45561 5.82374C7.40002 8.62527 7.099 11.3343 7.099 11.3343L6.50061 10.7359C6.32693 10.5622 6.03064 10.6391 5.96029 10.8745C5.23283 13.3075 5.71564 15.7967 5.71564 15.7967L5.71514 15.7971L6.34936 15.6745C6.34525 15.6529 5.97377 13.6573 6.44041 11.5883L6.94955 12.0966Z" fill="#D5EAFF"/>
<path d="M13.2564 6.037C13.3141 5.86841 13.224 5.68505 13.0554 5.62735C12.8865 5.57016 12.7031 5.65981 12.6457 5.82837C11.8359 8.19849 10.4981 10.3099 8.9927 12.1216C8.98586 10.394 9.52113 9.33536 9.5316 9.31513C9.61414 9.15759 9.55367 8.96271 9.39641 8.87966C9.23793 8.79677 9.04348 8.85712 8.96031 9.0147C8.92824 9.07575 8.19051 10.5135 8.37992 12.8306C4.65574 17.0054 0.236005 19.404 0.170849 19.4388C0.0136223 19.5228 -0.0459481 19.7181 0.0381926 19.8753C0.0961615 19.9842 0.207724 20.0461 0.323037 20.0461C0.374404 20.0461 0.426084 20.034 0.474599 20.0081C0.530303 19.9785 3.69996 18.2597 6.9675 15.1939C7.06207 15.2017 7.47437 15.2338 7.91512 15.2338C8.76023 15.2338 10.083 15.154 11.1436 14.7297C11.3091 14.6633 11.3897 14.4757 11.3236 14.3101C11.2574 14.1447 11.068 14.0648 10.9041 14.1303C9.80613 14.5696 8.34164 14.6003 7.6 14.5828C9.86817 12.335 12.09 9.45052 13.2564 6.037Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.78903C16.7022 7.53259 16.444 7.30438 16.1963 7.40341L15.166 7.81556C16.1394 3.67852 13.6255 1.19344 12.3309 0.22168L11.9303 0.747813C12.295 4.27137 10.8697 6.83704 10.8697 6.83704L9.6861 6.12438C7.63047 8.92591 7.26813 11.6985 7.26813 11.6985L6.22457 11.0113L5.84278 15.6606L4.19531 17.1728C4.19531 17.1728 9.12215 19.3283 13.862 14.9157C14.0281 14.7611 13.9924 14.4887 13.7895 14.3873L12.2619 13.6235C13.7729 12.725 15.8697 10.4494 16.629 7.78903Z" fill="#59A0FF"/>
<path d="M6.94955 12.1274C7.20748 12.385 7.64893 12.231 7.69061 11.8688L7.74029 11.4374C7.74315 11.413 8.03272 9.0499 9.75631 6.54548L10.3884 7.17751C10.5932 7.38228 10.9371 7.33408 11.0777 7.08091L11.2124 6.83837C11.2795 6.71798 12.7674 3.97458 12.3306 0.221802C12.2926 0.193287 12.2554 0.165709 12.2196 0.13981C11.9917 -0.0252682 11.68 0.155865 11.709 0.435826C12.0737 3.95939 10.6483 6.52505 10.6483 6.52505L9.94327 5.82001C9.80448 5.68122 9.57174 5.69626 9.45561 5.8545C7.40002 8.65603 7.099 11.3651 7.099 11.3651L6.50061 10.7667C6.32693 10.593 6.03064 10.6699 5.96029 10.9052C5.23283 13.3383 5.71564 15.8274 5.71564 15.8274L5.71514 15.8279L6.34936 15.7053C6.34525 15.6837 5.97377 13.688 6.44041 11.619L6.94955 12.1274Z" fill="#D5EAFF"/>
<path d="M13.2564 6.06776C13.3141 5.89917 13.224 5.71581 13.0554 5.65811C12.8865 5.60092 12.7031 5.69057 12.6457 5.85913C11.8359 8.22925 10.4981 10.3407 8.9927 12.1524C8.98586 10.4247 9.52113 9.36612 9.5316 9.34589C9.61414 9.18835 9.55367 8.99347 9.39641 8.91042C9.23793 8.82753 9.04348 8.88788 8.96031 9.04546C8.92824 9.10651 8.19051 10.5442 8.37992 12.8614C4.65574 17.0362 0.236005 19.4348 0.170849 19.4695C0.0136223 19.5535 -0.0459481 19.7489 0.0381926 19.9061C0.0961615 20.015 0.207724 20.0769 0.323037 20.0769C0.374404 20.0769 0.426084 20.0647 0.474599 20.0389C0.530303 20.0093 3.69996 18.2904 6.9675 15.2246C7.06207 15.2324 7.47437 15.2646 7.91512 15.2646C8.76023 15.2646 10.083 15.1847 11.1436 14.7604C11.3091 14.6941 11.3897 14.5064 11.3236 14.3409C11.2574 14.1754 11.068 14.0955 10.9041 14.1611C9.80613 14.6004 8.34164 14.6311 7.6 14.6136C9.86817 12.3658 12.09 9.48128 13.2564 6.06776Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.81979C16.7022 7.56335 16.444 7.33515 16.1963 7.43417L15.166 7.84632C16.1394 3.70928 13.6255 1.2242 12.3309 0.252441L11.9303 0.778575C12.295 4.30213 10.8697 6.8678 10.8697 6.8678L9.6861 6.15514C7.63047 8.95667 7.26813 11.7293 7.26813 11.7293L6.22457 11.042L5.84278 15.6913L4.19531 17.2036C4.19531 17.2036 9.12215 19.3591 13.862 14.9464C14.0281 14.7919 13.9924 14.5195 13.7895 14.418L12.2619 13.6543C13.7729 12.7558 15.8697 10.4802 16.629 7.81979Z" fill="#6AAAFF"/>
<path d="M6.94955 12.1581C7.20748 12.4157 7.64893 12.2618 7.69061 11.8996L7.74029 11.4682C7.74315 11.4438 8.03272 9.08066 9.75631 6.57624L10.3884 7.20828C10.5932 7.41304 10.9371 7.36484 11.0777 7.11167L11.2124 6.86913C11.2795 6.74874 12.7674 4.00534 12.3306 0.252564C12.2926 0.224048 12.2554 0.19647 12.2196 0.170572C11.9917 0.00549348 11.68 0.186627 11.709 0.466588C12.0737 3.99015 10.6483 6.55581 10.6483 6.55581L9.94327 5.85077C9.80448 5.71199 9.57174 5.72702 9.45561 5.88527C7.40002 8.68679 7.099 11.3958 7.099 11.3958L6.50061 10.7974C6.32693 10.6237 6.03064 10.7007 5.96029 10.936C5.23283 13.369 5.71564 15.8582 5.71564 15.8582L5.71514 15.8586L6.34936 15.7361C6.34525 15.7145 5.97377 13.7188 6.44041 11.6498L6.94955 12.1581Z" fill="#D5EAFF"/>
<path d="M13.2564 6.09852C13.3141 5.92993 13.224 5.74657 13.0554 5.68887C12.8865 5.63169 12.7031 5.72134 12.6457 5.88989C11.8359 8.26001 10.4981 10.3715 8.9927 12.1831C8.98586 10.4555 9.52113 9.39689 9.5316 9.37665C9.61414 9.21911 9.55367 9.02423 9.39641 8.94118C9.23793 8.85829 9.04348 8.91864 8.96031 9.07622C8.92824 9.13728 8.19051 10.575 8.37992 12.8922C4.65574 17.0669 0.236005 19.4656 0.170849 19.5003C0.0136223 19.5843 -0.0459481 19.7796 0.0381926 19.9369C0.0961615 20.0457 0.207724 20.1076 0.323037 20.1076C0.374404 20.1076 0.426084 20.0955 0.474599 20.0697C0.530303 20.04 3.69996 18.3212 6.9675 15.2554C7.06207 15.2632 7.47437 15.2954 7.91512 15.2954C8.76023 15.2954 10.083 15.2155 11.1436 14.7912C11.3091 14.7249 11.3897 14.5372 11.3236 14.3716C11.2574 14.2062 11.068 14.1263 10.9041 14.1919C9.80613 14.6312 8.34164 14.6618 7.6 14.6443C9.86817 12.3965 12.09 9.51204 13.2564 6.09852Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.85056C16.7022 7.59411 16.444 7.36591 16.1963 7.46493L15.166 7.87708C16.1394 3.74004 13.6255 1.25496 12.3309 0.283203L11.9303 0.809337C12.295 4.3329 10.8697 6.89856 10.8697 6.89856L9.6861 6.18591C7.63047 8.98743 7.26813 11.76 7.26813 11.76L6.22457 11.0728L5.84278 15.7221L4.19531 17.2344C4.19531 17.2344 9.12215 19.3899 13.862 14.9772C14.0281 14.8226 13.9924 14.5503 13.7895 14.4488L12.2619 13.685C13.7729 12.7865 15.8697 10.511 16.629 7.85056Z" fill="#8EBEFE"/>
<path d="M6.94955 12.1889C7.20748 12.4465 7.64893 12.2925 7.69061 11.9303L7.74029 11.499C7.74315 11.4746 8.03272 9.11142 9.75631 6.60701L10.3884 7.23904C10.5932 7.4438 10.9371 7.3956 11.0777 7.14244L11.2124 6.8999C11.2795 6.77951 12.7674 4.0361 12.3306 0.283326C12.2926 0.25481 12.2554 0.227232 12.2196 0.201334C11.9917 0.0362552 11.68 0.217388 11.709 0.49735C12.0737 4.02091 10.6483 6.58658 10.6483 6.58658L9.94327 5.88154C9.80448 5.74275 9.57174 5.75779 9.45561 5.91603C7.40002 8.71756 7.099 11.4266 7.099 11.4266L6.50061 10.8282C6.32693 10.6545 6.03064 10.7314 5.96029 10.9667C5.23283 13.3998 5.71564 15.889 5.71564 15.889L5.71514 15.8894L6.34936 15.7668C6.34525 15.7452 5.97377 13.7496 6.44041 11.6806L6.94955 12.1889Z" fill="#D5EAFF"/>
<path d="M13.2564 6.12928C13.3141 5.96069 13.224 5.77733 13.0554 5.71964C12.8865 5.66245 12.7031 5.7521 12.6457 5.92065C11.8359 8.29077 10.4981 10.4022 8.9927 12.2139C8.98586 10.4862 9.52113 9.42765 9.5316 9.40741C9.61414 9.24987 9.55367 9.05499 9.39641 8.97194C9.23793 8.88905 9.04348 8.9494 8.96031 9.10698C8.92824 9.16804 8.19051 10.6057 8.37992 12.9229C4.65574 17.0977 0.236005 19.4963 0.170849 19.5311C0.0136223 19.615 -0.0459481 19.8104 0.0381926 19.9676C0.0961615 20.0765 0.207724 20.1384 0.323037 20.1384C0.374404 20.1384 0.426084 20.1263 0.474599 20.1004C0.530303 20.0708 3.69996 18.352 6.9675 15.2862C7.06207 15.2939 7.47437 15.3261 7.91512 15.3261C8.76023 15.3261 10.083 15.2462 11.1436 14.822C11.3091 14.7556 11.3897 14.568 11.3236 14.4024C11.2574 14.2369 11.068 14.1571 10.9041 14.2226C9.80613 14.6619 8.34164 14.6926 7.6 14.6751C9.86817 12.4273 12.09 9.5428 13.2564 6.12928Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.88132C16.7022 7.62487 16.444 7.39667 16.1963 7.49569L15.166 7.90784C16.1394 3.77081 13.6255 1.28572 12.3309 0.313965L11.9303 0.840098C12.295 4.36366 10.8697 6.92932 10.8697 6.92932L9.6861 6.21667C7.63047 9.01819 7.26813 11.7908 7.26813 11.7908L6.22457 11.1035L5.84278 15.7529L4.19531 17.2651C4.19531 17.2651 9.12215 19.4206 13.862 15.008C14.0281 14.8534 13.9924 14.581 13.7895 14.4796L12.2619 13.7158C13.7729 12.8173 15.8697 10.5417 16.629 7.88132Z" fill="#A0C9FF"/>
<path d="M6.94955 12.2196C7.20748 12.4773 7.64893 12.3233 7.69061 11.9611L7.74029 11.5297C7.74315 11.5053 8.03272 9.14218 9.75631 6.63777L10.3884 7.2698C10.5932 7.47457 10.9371 7.42636 11.0777 7.1732L11.2124 6.93066C11.2795 6.81027 12.7674 4.06687 12.3306 0.314088C12.2926 0.285572 12.2554 0.257994 12.2196 0.232095C11.9917 0.0670169 11.68 0.24815 11.709 0.528111C12.0737 4.05167 10.6483 6.61734 10.6483 6.61734L9.94327 5.9123C9.80448 5.77351 9.57174 5.78855 9.45561 5.94679C7.40002 8.74832 7.099 11.4573 7.099 11.4573L6.50061 10.8589C6.32693 10.6853 6.03064 10.7622 5.96029 10.9975C5.23283 13.4305 5.71564 15.9197 5.71564 15.9197L5.71514 15.9202L6.34936 15.7976C6.34525 15.776 5.97377 13.7803 6.44041 11.7113L6.94955 12.2196Z" fill="#D5EAFF"/>
<path d="M13.2564 6.16005C13.3141 5.99145 13.224 5.80809 13.0554 5.7504C12.8865 5.69321 12.7031 5.78286 12.6457 5.95141C11.8359 8.32153 10.4981 10.433 8.9927 12.2447C8.98586 10.517 9.52113 9.45841 9.5316 9.43818C9.61414 9.28064 9.55367 9.08575 9.39641 9.00271C9.23793 8.91982 9.04348 8.98017 8.96031 9.13774C8.92824 9.1988 8.19051 10.6365 8.37992 12.9537C4.65574 17.1285 0.236005 19.5271 0.170849 19.5618C0.0136223 19.6458 -0.0459481 19.8412 0.0381926 19.9984C0.0961615 20.1073 0.207724 20.1692 0.323037 20.1692C0.374404 20.1692 0.426084 20.157 0.474599 20.1312C0.530303 20.1015 3.69996 18.3827 6.9675 15.3169C7.06207 15.3247 7.47437 15.3569 7.91512 15.3569C8.76023 15.3569 10.083 15.277 11.1436 14.8527C11.3091 14.7864 11.3897 14.5987 11.3236 14.4331C11.2574 14.2677 11.068 14.1878 10.9041 14.2534C9.80613 14.6927 8.34164 14.7233 7.6 14.7058C9.86817 12.4581 12.09 9.57357 13.2564 6.16005Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.91208C16.7022 7.65563 16.444 7.42743 16.1963 7.52645L15.166 7.9386C16.1394 3.80157 13.6255 1.31649 12.3309 0.344727L11.9303 0.87086C12.295 4.39442 10.8697 6.96009 10.8697 6.96009L9.6861 6.24743C7.63047 9.04896 7.26813 11.8215 7.26813 11.8215L6.22457 11.1343L5.84278 15.7836L4.19531 17.2959C4.19531 17.2959 9.12215 19.4514 13.862 15.0387C14.0281 14.8842 13.9924 14.6118 13.7895 14.5103L12.2619 13.7465C13.7729 12.8481 15.8697 10.5725 16.629 7.91208Z" fill="#CDE2FF"/>
<path d="M6.94955 12.2504C7.20748 12.508 7.64893 12.354 7.69061 11.9919L7.74029 11.5605C7.74315 11.5361 8.03272 9.17295 9.75631 6.66853L10.3884 7.30056C10.5932 7.50533 10.9371 7.45712 11.0777 7.20396L11.2124 6.96142C11.2795 6.84103 12.7674 4.09763 12.3306 0.344849C12.2926 0.316334 12.2554 0.288755 12.2196 0.262857C11.9917 0.0977786 11.68 0.278912 11.709 0.558873C12.0737 4.08243 10.6483 6.6481 10.6483 6.6481L9.94327 5.94306C9.80448 5.80427 9.57174 5.81931 9.45561 5.97755C7.40002 8.77908 7.099 11.4881 7.099 11.4881L6.50061 10.8897C6.32693 10.716 6.03064 10.7929 5.96029 11.0283C5.23283 13.4613 5.71564 15.9505 5.71564 15.9505L5.71514 15.9509L6.34936 15.8283C6.34525 15.8067 5.97377 13.8111 6.44041 11.7421L6.94955 12.2504Z" fill="#D5EAFF"/>
<path d="M13.2564 6.19081C13.3141 6.02221 13.224 5.83885 13.0554 5.78116C12.8865 5.72397 12.7031 5.81362 12.6457 5.98218C11.8359 8.35229 10.4981 10.4637 8.9927 12.2754C8.98586 10.5478 9.52113 9.48917 9.5316 9.46894C9.61414 9.3114 9.55367 9.11651 9.39641 9.03347C9.23793 8.95058 9.04348 9.01093 8.96031 9.16851C8.92824 9.22956 8.19051 10.6673 8.37992 12.9844C4.65574 17.1592 0.236005 19.5579 0.170849 19.5926C0.0136223 19.6766 -0.0459481 19.8719 0.0381926 20.0291C0.0961615 20.138 0.207724 20.1999 0.323037 20.1999C0.374404 20.1999 0.426084 20.1878 0.474599 20.162C0.530303 20.1323 3.69996 18.4135 6.9675 15.3477C7.06207 15.3555 7.47437 15.3877 7.91512 15.3877C8.76023 15.3877 10.083 15.3078 11.1436 14.8835C11.3091 14.8171 11.3897 14.6295 11.3236 14.4639C11.2574 14.2985 11.068 14.2186 10.9041 14.2841C9.80613 14.7234 8.34164 14.7541 7.6 14.7366C9.86817 12.4888 12.09 9.60433 13.2564 6.19081Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.94333C16.7022 7.68688 16.444 7.45868 16.1963 7.5577L15.166 7.96985C16.1394 3.83282 13.6255 1.34774 12.3309 0.375977L11.9303 0.90211C12.295 4.42567 10.8697 6.99134 10.8697 6.99134L9.6861 6.27868C7.63047 9.08021 7.26813 11.8528 7.26813 11.8528L6.22457 11.1656L5.84278 15.8149L4.19531 17.3271C4.19531 17.3271 9.12215 19.4826 13.862 15.07C14.0281 14.9154 13.9924 14.643 13.7895 14.5416L12.2619 13.7778C13.7729 12.8793 15.8697 10.6037 16.629 7.94333Z" fill="#DCEBFF"/>
<path d="M6.94955 12.2814C7.20748 12.539 7.64893 12.385 7.69061 12.0229L7.74029 11.5915C7.74315 11.5671 8.03272 9.20395 9.75631 6.69953L10.3884 7.33157C10.5932 7.53633 10.9371 7.48813 11.0777 7.23496L11.2124 6.99243C11.2795 6.87204 12.7674 4.12863 12.3306 0.375855C12.2926 0.347339 12.2554 0.319761 12.2196 0.293863C11.9917 0.128785 11.68 0.309918 11.709 0.589879C12.0737 4.11344 10.6483 6.67911 10.6483 6.67911L9.94327 5.97407C9.80448 5.83528 9.57174 5.85032 9.45561 6.00856C7.40002 8.81008 7.099 11.5191 7.099 11.5191L6.50061 10.9207C6.32693 10.747 6.03064 10.824 5.96029 11.0593C5.23283 13.4923 5.71564 15.9815 5.71564 15.9815L5.71514 15.9819L6.34936 15.8594C6.34525 15.8377 5.97377 13.8421 6.44041 11.7731L6.94955 12.2814Z" fill="#D5EAFF"/>
<path d="M13.2564 6.22181C13.3141 6.05322 13.224 5.86986 13.0554 5.81217C12.8865 5.75498 12.7031 5.84463 12.6457 6.01318C11.8359 8.3833 10.4981 10.4947 8.9927 12.3064C8.98586 10.5788 9.52113 9.52018 9.5316 9.49994C9.61414 9.3424 9.55367 9.14752 9.39641 9.06447C9.23793 8.98158 9.04348 9.04193 8.96031 9.19951C8.92824 9.26057 8.19051 10.6983 8.37992 13.0155C4.65574 17.1902 0.236005 19.5889 0.170849 19.6236C0.0136223 19.7076 -0.0459481 19.9029 0.0381926 20.0602C0.0961615 20.169 0.207724 20.2309 0.323037 20.2309C0.374404 20.2309 0.426084 20.2188 0.474599 20.193C0.530303 20.1633 3.69996 18.4445 6.9675 15.3787C7.06207 15.3865 7.47437 15.4187 7.91512 15.4187C8.76023 15.4187 10.083 15.3388 11.1436 14.9145C11.3091 14.8482 11.3897 14.6605 11.3236 14.4949C11.2574 14.3295 11.068 14.2496 10.9041 14.3151C9.80613 14.7544 8.34164 14.7851 7.6 14.7676C9.86817 12.5198 12.09 9.63533 13.2564 6.22181Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.629 7.97409C16.7022 7.71765 16.444 7.48944 16.1963 7.58847L15.166 8.00061C16.1394 3.86358 13.6255 1.3785 12.3309 0.406738L11.9303 0.932872C12.295 4.45643 10.8697 7.0221 10.8697 7.0221L9.6861 6.30944C7.63047 9.11097 7.26813 11.8835 7.26813 11.8835L6.22457 11.1963L5.84278 15.8456L4.19531 17.3579C4.19531 17.3579 9.12215 19.5134 13.862 15.1007C14.0281 14.9462 13.9924 14.6738 13.7895 14.5723L12.2619 13.8086C13.7729 12.9101 15.8697 10.6345 16.629 7.97409Z" fill="#EAF3FF"/>
<path d="M6.94955 12.3122C7.20748 12.5698 7.64893 12.4158 7.69061 12.0536L7.74029 11.6223C7.74315 11.5978 8.03272 9.23471 9.75631 6.7303L10.3884 7.36233C10.5932 7.56709 10.9371 7.51889 11.0777 7.26573L11.2124 7.02319C11.2795 6.9028 12.7674 4.15939 12.3306 0.406617C12.2926 0.378101 12.2554 0.350523 12.2196 0.324625C11.9917 0.159546 11.68 0.340679 11.709 0.620641C12.0737 4.1442 10.6483 6.70987 10.6483 6.70987L9.94327 6.00483C9.80448 5.86604 9.57174 5.88108 9.45561 6.03932C7.40002 8.84085 7.099 11.5499 7.099 11.5499L6.50061 10.9515C6.32693 10.7778 6.03064 10.8547 5.96029 11.09C5.23283 13.5231 5.71564 16.0123 5.71564 16.0123L5.71514 16.0127L6.34936 15.8901C6.34525 15.8685 5.97377 13.8728 6.44041 11.8039L6.94955 12.3122Z" fill="#D5EAFF"/>
<path d="M13.2564 6.25258C13.3141 6.08398 13.224 5.90062 13.0554 5.84293C12.8865 5.78574 12.7031 5.87539 12.6457 6.04394C11.8359 8.41406 10.4981 10.5255 8.9927 12.3372C8.98586 10.6095 9.52113 9.55094 9.5316 9.5307C9.61414 9.37317 9.55367 9.17828 9.39641 9.09524C9.23793 9.01234 9.04348 9.0727 8.96031 9.23027C8.92824 9.29133 8.19051 10.729 8.37992 13.0462C4.65574 17.221 0.236005 19.6196 0.170849 19.6543C0.0136223 19.7383 -0.0459481 19.9337 0.0381926 20.0909C0.0961615 20.1998 0.207724 20.2617 0.323037 20.2617C0.374404 20.2617 0.426084 20.2495 0.474599 20.2237C0.530303 20.1941 3.69996 18.4752 6.9675 15.4095C7.06207 15.4172 7.47437 15.4494 7.91512 15.4494C8.76023 15.4494 10.083 15.3695 11.1436 14.9452C11.3091 14.8789 11.3897 14.6913 11.3236 14.5257C11.2574 14.3602 11.068 14.2804 10.9041 14.3459C9.80613 14.7852 8.34164 14.8159 7.6 14.7984C9.86817 12.5506 12.09 9.6661 13.2564 6.25258Z" fill="#B8DDFF"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,10 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.61417 2.15625H0.863281V15.9173H1.61417V2.15625Z" fill="#FCD577"/>
<path d="M19.6761 20.2188H5.91504V20.9696H19.6761V20.2188Z" fill="#FF6F52"/>
<path d="M2.47774 2.53177L1.23889 0.541992L0 2.53177H2.47774Z" fill="#FCD577"/>
<path d="M0 15.5425L1.23889 17.5322L2.47774 15.5425H0Z" fill="#FCD577"/>
<path d="M19.3018 21.8332L21.2915 20.5944L19.3018 19.3555V21.8332Z" fill="#FF6F52"/>
<path d="M6.29154 21.8332L4.30176 20.5944L6.29154 19.3555V21.8332Z" fill="#FF6F52"/>
<path d="M21.2917 1.72397V0.541992H4.30176V17.5319H21.2917V2.97543" fill="#CFDCE5"/>
<path d="M3.92578 0.166504V17.9073H21.6665V0.166504H3.92578ZM20.9157 10.1588V17.1564H10.1888V14.9644H9.438V17.1564H4.67662V0.917389H9.438V9.40788V10.1588V13.0121H10.1889V10.1588H17.247V9.40788H10.1889V0.917389H20.9157V9.40788H19.0493V10.1588H20.9157Z" fill="#415E72"/>
</svg>

After

Width:  |  Height:  |  Size: 935 B

View File

@ -0,0 +1,18 @@
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.8333 1.71851L0.472669 15.9196C-0.702346 17.5301 0.447998 19.7934 2.44166 19.7934H19.225C21.2187 19.7934 22.369 17.5301 21.194 15.9196L10.8333 1.71851Z" fill="#F1FAFF"/>
<path d="M21.1931 15.9183L10.8333 1.71851L9.06055 4.14839L17.6475 15.9183C18.8229 17.5294 17.6722 19.7933 15.6779 19.7933H19.2235C21.2178 19.7934 22.3685 17.5294 21.1931 15.9183Z" fill="#C7EEFB"/>
<path d="M12.9102 6.05994H8.76926C7.63838 6.05994 6.72168 5.14319 6.72168 4.01236V2.12199C6.72168 2.00515 6.81643 1.9104 6.93327 1.9104H14.7463C14.8631 1.9104 14.9579 2.00515 14.9579 2.12199V4.01236C14.9579 5.14319 14.0411 6.05994 12.9102 6.05994Z" fill="#E0DDE2"/>
<path d="M14.748 1.9104H13.5893V2.63688C13.5893 3.77034 12.6704 4.6892 11.537 4.6892H7.40546C7.19886 4.6892 6.99959 4.65835 6.81152 4.6016C7.06628 5.44539 7.84934 6.05998 8.77624 6.05998H12.9078C14.0412 6.05998 14.9601 5.14112 14.9601 4.00766V2.12246C14.9601 2.00536 14.8651 1.9104 14.748 1.9104Z" fill="#C8C1C9"/>
<path d="M16.3024 2.66033H5.37858C5.26174 2.66033 5.16699 2.56558 5.16699 2.44874V0.41838C5.16699 0.301538 5.26174 0.206787 5.37858 0.206787H16.3024C16.4192 0.206787 16.514 0.301538 16.514 0.41838V2.44874C16.514 2.56562 16.4193 2.66033 16.3024 2.66033Z" fill="#E0DDE2"/>
<path d="M16.3019 0.206787H15.0629V0.997127C15.0629 1.11426 14.9679 1.20923 14.8508 1.20923H5.16699V2.44827C5.16699 2.56541 5.26195 2.66037 5.37909 2.66037H16.3019C16.4191 2.66037 16.514 2.56541 16.514 2.44827V0.418887C16.514 0.30175 16.4191 0.206787 16.3019 0.206787Z" fill="#C8C1C9"/>
<path d="M10.841 4.82612C11.141 4.82612 11.3842 4.58293 11.3842 4.28294C11.3842 3.98294 11.141 3.73975 10.841 3.73975C10.541 3.73975 10.2979 3.98294 10.2979 4.28294C10.2979 4.58293 10.541 4.82612 10.841 4.82612Z" fill="#FA2A3B"/>
<path d="M10.7136 10.2642C11.2545 10.2642 11.6929 9.82577 11.6929 9.28494C11.6929 8.7441 11.2545 8.30566 10.7136 8.30566C10.1728 8.30566 9.73438 8.7441 9.73438 9.28494C9.73438 9.82577 10.1728 10.2642 10.7136 10.2642Z" fill="#62D8F9"/>
<path d="M10.7138 8.30542C10.6442 8.30542 10.5762 8.31283 10.5107 8.32662C10.9541 8.42014 11.2869 8.81349 11.2869 9.28469C11.2869 9.7559 10.9541 10.1492 10.5107 10.2428C10.5763 10.2566 10.6442 10.264 10.7138 10.264C11.2546 10.264 11.6931 9.82551 11.6931 9.28469C11.6931 8.74387 11.2547 8.30542 10.7138 8.30542Z" fill="#00BEF7"/>
<path d="M14.1217 13.3878L12.4086 13.1093L11.4778 11.3114C11.3944 11.1233 11.2634 10.8659 10.9741 10.7797L9.88702 10.4535C9.76776 10.4245 9.65308 10.4138 9.4497 10.4457L7.13145 10.9842C6.99264 11.0164 6.86865 11.0944 6.7794 11.2054L5.54163 12.746C5.32157 13.02 5.3652 13.4204 5.63913 13.6405C5.75661 13.7349 5.89736 13.7808 6.03718 13.7808C6.22338 13.7808 6.40789 13.6995 6.53357 13.543L7.6336 12.1739L8.72165 11.9212L8.08273 14.0505C8.07045 14.0863 8.06652 14.1326 8.04896 14.2338L7.68849 16.7926L6.00734 18.2512C5.74192 18.4815 5.71344 18.8834 5.94374 19.1488C6.06959 19.2938 6.24657 19.368 6.4246 19.368C6.57238 19.368 6.72087 19.3168 6.84127 19.2124L8.70227 17.5977C8.81826 17.4971 8.89393 17.358 8.91534 17.2059L9.21009 15.1137L9.24932 15.1255L10.8419 16.9842L11.7847 19.0311C11.892 19.264 12.1223 19.4013 12.363 19.4013C12.452 19.4013 12.5425 19.3825 12.6287 19.3428C12.9479 19.1958 13.0874 18.8179 12.9405 18.4987L11.9606 16.3713C11.936 16.3179 11.9042 16.2682 11.8659 16.2236L10.5325 14.6674L10.9911 13.1391L11.4257 13.9786C11.5183 14.1574 11.69 14.2818 11.8886 14.3141L13.9174 14.6439C13.952 14.6495 13.9864 14.6522 14.0203 14.6522C14.3267 14.6522 14.5968 14.4302 14.6475 14.118C14.7039 13.771 14.4685 13.4442 14.1217 13.3878Z" fill="#62D8F9"/>
<path d="M8.99707 10.5509C9.20045 10.519 9.31454 10.5319 9.4338 10.5609L10.5209 10.8871C10.8102 10.9732 10.9411 11.2307 11.0245 11.4187L12.0585 13.427C12.085 13.4785 12.1345 13.5142 12.1917 13.523L14.645 13.9015C14.5979 13.6442 14.3947 13.4322 14.1218 13.3878L12.4088 13.1093L11.4779 11.3114C11.3945 11.1233 11.2635 10.8659 10.9742 10.7797L9.88716 10.4535C9.7679 10.4245 9.65322 10.4138 9.44984 10.4457L8.99707 10.5509Z" fill="#00BEF7"/>
<path d="M11.9615 16.371C11.9369 16.3176 11.905 16.2679 11.8667 16.2233L10.5333 14.6671L10.9919 13.1391L10.9337 13.0266C10.8579 12.8802 10.6424 12.9003 10.595 13.0583L10.1416 14.5691C10.1032 14.6971 10.1325 14.8358 10.2194 14.9372L11.4134 16.3307C11.4516 16.3753 11.4835 16.4251 11.5081 16.4784L12.4879 18.6058C12.6191 18.8907 12.5216 19.2219 12.2723 19.3943C12.3025 19.3987 12.3331 19.401 12.3638 19.401C12.4528 19.401 12.5433 19.3822 12.6295 19.3425C12.9487 19.1955 13.0882 18.8176 12.9413 18.4985L11.9615 16.371Z" fill="#00BEF7"/>
<path d="M3.47827 18.4408C3.31064 18.4408 3.17032 18.3097 3.16092 18.1403C3.08839 16.8332 4.09282 15.7107 5.39991 15.6381C5.57574 15.6291 5.72551 15.7627 5.7352 15.9381C5.74493 16.1135 5.61065 16.2637 5.4352 16.2734C4.47834 16.3266 3.74305 17.1483 3.79616 18.105C3.8059 18.2805 3.67162 18.4306 3.49617 18.4403C3.4902 18.4406 3.48419 18.4408 3.47827 18.4408Z" fill="#00BEF7"/>
<path d="M4.76838 18.4541C4.60076 18.4541 4.46044 18.3231 4.45104 18.1536C4.41524 17.5088 4.91082 16.9549 5.5557 16.9192C5.73081 16.9095 5.88121 17.0438 5.89094 17.2192C5.90068 17.3947 5.76636 17.5447 5.59091 17.5545C5.29634 17.5708 5.06994 17.8238 5.08627 18.1185C5.09601 18.2939 4.96169 18.444 4.78624 18.4537C4.78032 18.454 4.77431 18.4541 4.76838 18.4541Z" fill="#00BEF7"/>
<path d="M17.0106 13.6224C17.0076 13.6224 17.0046 13.6223 17.0016 13.6222C16.826 13.6173 16.6876 13.471 16.6925 13.2954C16.7191 12.3374 15.9615 11.5363 15.0036 11.5097C14.828 11.5048 14.6896 11.3584 14.6945 11.1828C14.6994 11.0072 14.8466 10.8688 15.0213 10.8737C16.3299 10.9101 17.365 12.0044 17.3285 13.3131C17.3237 13.4857 17.1822 13.6224 17.0106 13.6224Z" fill="#00BEF7"/>
<path d="M15.7216 13.6711C15.7186 13.6711 15.7156 13.6711 15.7126 13.671C15.537 13.6661 15.3986 13.5197 15.4035 13.3441C15.4075 13.2013 15.3555 13.0654 15.2574 12.9615C15.1591 12.8577 15.0263 12.7983 14.8835 12.7943C14.7079 12.7894 14.5695 12.6431 14.5743 12.4675C14.5793 12.2919 14.724 12.1531 14.9012 12.1583C15.214 12.1671 15.5046 12.2971 15.7196 12.5244C15.9346 12.7517 16.0482 13.0491 16.0395 13.3619C16.0346 13.5344 15.8932 13.6711 15.7216 13.6711Z" fill="#00BEF7"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,6 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.7715 21.8328C16.7205 21.8328 21.5431 16.9829 21.5431 11.0003C21.5431 5.01761 16.7205 0.167725 10.7715 0.167725C4.82259 0.167725 0 5.01761 0 11.0003C0 16.9829 4.82259 21.8328 10.7715 21.8328Z" fill="#B3CEEC"/>
<path d="M9.41996 16.2738H7.27293C7.10438 16.2738 6.96777 16.1371 6.96777 15.9686V6.03098C6.96777 5.86243 7.10442 5.72583 7.27293 5.72583H9.41996C9.58851 5.72583 9.72511 5.86247 9.72511 6.03098V15.9686C9.72511 16.1372 9.58851 16.2738 9.41996 16.2738Z" fill="#5F99D7"/>
<path d="M14.3946 16.2738H12.2475C12.079 16.2738 11.9424 16.1371 11.9424 15.9686V6.03098C11.9424 5.86243 12.079 5.72583 12.2475 5.72583H14.3946C14.5631 5.72583 14.6997 5.86247 14.6997 6.03098V15.9686C14.6997 16.1372 14.5631 16.2738 14.3946 16.2738Z" fill="#5F99D7"/>
<path d="M10.8332 0.166748C10.584 0.166748 10.3369 0.175931 10.0918 0.19252C15.7289 0.573633 20.1837 5.26629 20.1837 11.0001C20.1837 16.7339 15.7289 21.4265 10.0918 21.8076C10.3369 21.8242 10.584 21.8334 10.8332 21.8334C16.8163 21.8334 21.6666 16.9832 21.6666 11.0001C21.6666 5.01699 16.8163 0.166748 10.8332 0.166748Z" fill="#98BCE5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,6 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.8334 21.8332C7.93969 21.8332 5.21921 20.7063 3.17302 18.6601C1.12688 16.614 0 13.8935 0 10.9999C0 8.10619 1.12688 5.38572 3.17298 3.33953C5.21921 1.29338 7.93961 0.166504 10.8334 0.166504C13.727 0.166504 16.4475 1.29338 18.4936 3.33953C20.5398 5.38572 21.6667 8.10615 21.6667 10.9999C21.6667 13.8936 20.5397 16.614 18.4936 18.6602C16.4475 20.7064 13.727 21.8332 10.8334 21.8332ZM10.8334 3.12028C6.48853 3.12028 2.95378 6.65504 2.95378 10.9999C2.95378 15.3447 6.48853 18.8794 10.8334 18.8794C15.1782 18.8794 18.713 15.3447 18.713 10.9999C18.713 6.65508 15.1781 3.12028 10.8334 3.12028Z" fill="#023DFE" fill-opacity="0.6"/>
<g opacity="0.1">
<path d="M5.14215 18.6601C3.09605 16.614 1.96917 13.8935 1.96917 10.9998C1.96917 8.10615 3.09605 5.38567 5.14215 3.33957C6.95445 1.52727 9.29585 0.436914 11.8179 0.211149C11.4924 0.18195 11.1641 0.166504 10.8334 0.166504C7.93969 0.166504 5.21921 1.29338 3.17302 3.33953C1.12688 5.38563 0 8.10615 0 10.9998C0 13.8934 1.12688 16.614 3.17298 18.6601C5.21913 20.7062 7.93961 21.8332 10.8333 21.8332C11.164 21.8332 11.4924 21.8177 11.8179 21.7886C9.29585 21.5628 6.95449 20.4724 5.14215 18.6601Z" fill="black"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class EmptySearchResultWidget extends StatelessWidget {
const EmptySearchResultWidget({
this.message = 'No results found',
super.key,
});
final String message;
@override
Widget build(BuildContext context) {
return Center(
child: Text(
message,
textAlign: TextAlign.center,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontWeight: FontWeight.w400,
),
),
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class SidebarCommunitiesList extends StatelessWidget {
const SidebarCommunitiesList({
required this.communities,
required this.itemBuilder,
required this.scrollController,
required this.onScrollToEnd,
super.key,
});
final List<CommunityModel> communities;
final Widget Function(BuildContext context, int index) itemBuilder;
final ScrollController scrollController;
final void Function() onScrollToEnd;
bool _onNotification(ScrollEndNotification notification) {
final hasReachedEnd = notification.metrics.extentAfter == 0;
if (hasReachedEnd) {
onScrollToEnd.call();
return true;
}
return false;
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: context.screenWidth * 0.5,
child: Scrollbar(
scrollbarOrientation: ScrollbarOrientation.left,
thumbVisibility: true,
controller: scrollController,
child: NotificationListener<ScrollEndNotification>(
onNotification: _onNotification,
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsetsDirectional.only(start: 16),
itemCount: communities.length,
controller: scrollController,
itemBuilder: itemBuilder,
),
),
),
),
);
}
}

View File

@ -16,12 +16,12 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da
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';
Future<void> main() async {
try {
const environment =
String.fromEnvironment('FLAVOR', defaultValue: 'development');
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
@ -33,9 +33,7 @@ Future<void> main() async {
}
class MyApp extends StatelessWidget {
MyApp({
super.key,
});
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
@ -56,11 +54,10 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<CreateRoutineBloc>(
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(),
),
@ -81,6 +78,8 @@ class MyApp extends StatelessWidget {
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));

87
lib/main_staging.dart Normal file
View File

@ -0,0 +1,87 @@
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_prod.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/space_tree/bloc/space_tree_event.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';
Future<void> main() async {
try {
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsStaging.currentPlatform,
);
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 {
String 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()..add(InitialEvent()),
),
],
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,
));
}
}

View File

@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
import 'package:syncrow_web/services/auth_api.dart';
@ -432,9 +433,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
static Future<void> logout(BuildContext context) async {
final storage = FlutterSecureStorage();
ProjectManager.clearProjectUUID();
const storage = FlutterSecureStorage();
context.read<SpaceTreeBloc>().add(ClearAllData());
storage.deleteAll();
user = null;
context.read<HomeBloc>().user = null;
await Future.wait<void>([
ProjectManager.clearProjectUUID(),
storage.deleteAll(),
]);
}
}

View File

@ -201,20 +201,17 @@ class ForgetPasswordWebPage extends StatelessWidget {
!state.isButtonEnabled &&
state.remainingTime != 1
? null
: () {
:
() {
if (forgetBloc
.forgetEmailKey.currentState!
.validate() ||
forgetBloc
.forgetRegionKey.currentState!
.validate()) {
if (forgetBloc
.forgetRegionKey.currentState!
.validate()) {
forgetBloc.add(StartTimerEvent());
}
.forgetEmailKey
.currentState!
.validate()) {
forgetBloc.add(
StartTimerEvent());
}
},
child: Text(
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
style: TextStyle(

View File

@ -55,12 +55,12 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
final isSmallScreen = isSmallScreenSize(context);
final isMediumScreen = isMediumScreenSize(context);
Size size = MediaQuery.of(context).size;
late ScrollController _scrollController;
_scrollController = ScrollController();
late ScrollController scrollController;
scrollController = ScrollController();
void _scrollToCenter() {
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
_scrollController.animateTo(
void scrollToCenter() {
final double middlePosition = scrollController.position.maxScrollExtent / 2;
scrollController.animateTo(
middlePosition,
duration: const Duration(seconds: 1),
curve: Curves.easeInOut,
@ -68,7 +68,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_scrollToCenter();
scrollToCenter();
});
return Stack(
@ -76,7 +76,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
FirstLayer(
second: Center(
child: ListView(
controller: _scrollController,
controller: scrollController,
shrinkWrap: true,
children: [
Container(
@ -199,7 +199,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
width: size.width * 0.9,
child: DropdownButtonHideUnderline(
child: DropdownButton2<String>(
style: TextStyle(color: Colors.black),
style: const TextStyle(color: Colors.black),
isExpanded: true,
hint: Text(
'Select your region/country',
@ -336,6 +336,16 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
obscureText: loginBloc.obscureText,
keyboardType: TextInputType.visiblePassword,
controller: loginBloc.loginPasswordController,
onFieldSubmitted: (value) {
if (loginBloc.loginFormKey.currentState!.validate()) {
loginBloc.add(LoginButtonPressed(
username: loginBloc.loginEmailController.text,
password: value,
));
} else {
loginBloc.add(ChangeValidateEvent());
}
},
decoration: textBoxDecoration()!.copyWith(
hintText: 'At least 8 characters',
hintStyle: Theme.of(context)
@ -393,7 +403,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
Transform.scale(
scale: 1.2,
child: Checkbox(
fillColor: MaterialStateProperty.all<Color>(Colors.white),
fillColor: WidgetStateProperty.all<Color>(Colors.white),
activeColor: Colors.white,
value: loginBloc.isChecked,
checkColor: Colors.black,

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class SearchResetButtons extends StatelessWidget {
const SearchResetButtons({
@ -17,8 +17,10 @@ class SearchResetButtons extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 25),

View File

@ -13,6 +13,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
late AcStatusModel deviceStatus;
final String deviceId;
Timer? _timer;
Timer? _countdownTimer;
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
@ -21,7 +22,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
on<AcBatchControlEvent>(_onAcBatchControl);
on<AcFactoryResetEvent>(_onFactoryReset);
on<AcStatusUpdated>(_onAcStatusUpdated);
on<OnClose>(_onClose);
on<IncreaseTimeEvent>(_handleIncreaseTime);
on<DecreaseTimeEvent>(_handleDecreaseTime);
on<UpdateTimerEvent>(_handleUpdateTimer);
on<ToggleScheduleEvent>(_handleToggleTimer);
on<ApiCountdownValueEvent>(_handleApiCountdownValue);
}
bool timerActive = false;
int scheduledHours = 0;
int scheduledMinutes = 0;
FutureOr<void> _onFetchAcStatus(
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
@ -30,8 +40,23 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.countdown1 != 0) {
// Convert API value to minutes
final totalMinutes = deviceStatus.countdown1 * 6;
scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
timerActive = true;
_startCountdownTimer(emit);
}
emit(ACStatusLoaded(
status: deviceStatus,
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
isTimerActive: timerActive,
));
_listenToChanges(event.deviceId);
emit(ACStatusLoaded(deviceStatus));
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
@ -70,31 +95,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
deviceStatus = event.deviceStatus;
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
// Future<void> testFirebaseConnection() async {
// // Reference to a test node in your database
// final testRef = FirebaseDatabase.instance.ref("test");
// // Write a test value
// await testRef.set("Hello, Firebase!");
// // Listen for changes on the test node
// testRef.onValue.listen((DatabaseEvent event) {
// final data = event.snapshot.value;
// print("Data from Firebase: $data");
// // If you see "Hello, Firebase!" printed in your console, it means the connection works.
// });
// }
FutureOr<void> _onAcControl(
AcControlEvent event, Emitter<AcsState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
await _runDebounce(
isBatch: false,
@ -151,7 +161,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
void _revertValueAndEmit(
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
_updateLocalValue(code, oldValue, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
@ -184,11 +194,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
if (value is bool) {
deviceStatus = deviceStatus.copyWith(childLock: value);
}
case 'countdown_time':
if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value);
}
break;
default:
break;
}
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
}
dynamic _getValueByCode(String code) {
@ -203,6 +218,8 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
return deviceStatus.fanSpeedsString;
case 'child_lock':
return deviceStatus.childLock;
case 'countdown_time':
return deviceStatus.countdown1;
default:
return null;
}
@ -216,7 +233,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus =
AcStatusModel.fromJson(event.devicesIds.first, status.status);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
@ -228,7 +245,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
_updateLocalValue(event.code, event.value, emit);
emit(ACStatusLoaded(deviceStatus));
emit(ACStatusLoaded(status: deviceStatus));
await _runDebounce(
isBatch: true,
@ -257,4 +274,144 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
emit(AcsFailedState(error: e.toString()));
}
}
void _onClose(OnClose event, Emitter<AcsState> emit) {
_countdownTimer?.cancel();
_timer?.cancel();
}
void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
int newHours = scheduledHours;
int newMinutes = scheduledMinutes + 30;
newHours += newMinutes ~/ 60;
newMinutes = newMinutes % 60;
if (newHours > 23) {
newHours = 23;
newMinutes = 59;
}
scheduledHours = newHours;
scheduledMinutes = newMinutes;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
));
}
void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
totalMinutes = (totalMinutes - 30).clamp(0, 1440);
scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
));
}
Future<void> _handleToggleTimer(
ToggleScheduleEvent event, Emitter<AcsState> emit) async {
if (state is! ACStatusLoaded) return;
final currentState = state as ACStatusLoaded;
timerActive = !timerActive;
if (timerActive) {
final totalMinutes = scheduledHours * 60 + scheduledMinutes;
if (totalMinutes <= 0) {
timerActive = false;
emit(currentState.copyWith(isTimerActive: timerActive));
return;
}
try {
final scaledValue = totalMinutes ~/ 6;
await _runDebounce(
isBatch: false,
deviceId: deviceId,
code: 'countdown_time',
value: scaledValue,
oldValue: scaledValue,
emit: emit,
);
_startCountdownTimer(emit);
emit(currentState.copyWith(isTimerActive: timerActive));
} catch (e) {
timerActive = false;
emit(AcsFailedState(error: e.toString()));
}
} else {
await _runDebounce(
isBatch: false,
deviceId: deviceId,
code: 'countdown_time',
value: 0,
oldValue: 0,
emit: emit,
);
_countdownTimer?.cancel();
scheduledHours = 0;
scheduledMinutes = 0;
emit(currentState.copyWith(
isTimerActive: timerActive,
scheduledHours: 0,
scheduledMinutes: 0,
));
}
}
void _startCountdownTimer(Emitter<AcsState> emit) {
_countdownTimer?.cancel();
int totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60);
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (totalSeconds > 0) {
totalSeconds--;
scheduledHours = totalSeconds ~/ 3600;
scheduledMinutes = (totalSeconds % 3600) ~/ 60;
add(UpdateTimerEvent());
} else {
_countdownTimer?.cancel();
timerActive = false;
scheduledHours = 0;
scheduledMinutes = 0;
add(TimerCompletedEvent());
}
});
}
void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
if (state is ACStatusLoaded) {
final currentState = state as ACStatusLoaded;
emit(currentState.copyWith(
scheduledHours: scheduledHours,
scheduledMinutes: scheduledMinutes,
isTimerActive: timerActive,
));
}
}
void _handleApiCountdownValue(
ApiCountdownValueEvent event, Emitter<AcsState> emit) {
if (state is ACStatusLoaded) {
final totalMinutes = event.apiValue * 6;
final scheduledHours = totalMinutes ~/ 60;
scheduledMinutes = totalMinutes % 60;
_startCountdownTimer(
emit,
);
add(UpdateTimerEvent());
}
}
@override
Future<void> close() {
add(OnClose());
return super.close();
}
}

View File

@ -8,6 +8,7 @@ sealed class AcsEvent extends Equatable {
@override
List<Object> get props => [];
}
class AcUpdated extends AcsEvent {}
class AcFetchDeviceStatusEvent extends AcsEvent {
@ -18,10 +19,12 @@ class AcFetchDeviceStatusEvent extends AcsEvent {
@override
List<Object> get props => [deviceId];
}
class AcStatusUpdated extends AcsEvent {
final AcStatusModel deviceStatus;
AcStatusUpdated(this.deviceStatus);
}
class AcFetchBatchStatusEvent extends AcsEvent {
final List<String> devicesIds;
@ -73,3 +76,30 @@ class AcFactoryResetEvent extends AcsEvent {
@override
List<Object> get props => [deviceId, factoryResetModel];
}
class OnClose extends AcsEvent {}
class IncreaseTimeEvent extends AcsEvent {
@override
List<Object> get props => [];
}
class DecreaseTimeEvent extends AcsEvent {
@override
List<Object> get props => [];
}
class ToggleScheduleEvent extends AcsEvent {}
class TimerCompletedEvent extends AcsEvent {}
class UpdateTimerEvent extends AcsEvent {
}
class ApiCountdownValueEvent extends AcsEvent {
final int apiValue;
const ApiCountdownValueEvent(this.apiValue);
}

View File

@ -2,8 +2,9 @@ import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
abstract class AcsState extends Equatable {
const AcsState();
final bool isTimerActive;
const AcsState({this.isTimerActive = false});
@override
List<Object> get props => [];
}
@ -15,8 +16,30 @@ class AcsLoadingState extends AcsState {}
class ACStatusLoaded extends AcsState {
final AcStatusModel status;
final DateTime timestamp;
final int scheduledHours;
final int scheduledMinutes;
final bool isTimerActive;
ACStatusLoaded(this.status) : timestamp = DateTime.now();
ACStatusLoaded({
required this.status,
this.scheduledHours = 0,
this.scheduledMinutes = 0,
this.isTimerActive = false,
}) : timestamp = DateTime.now();
ACStatusLoaded copyWith({
AcStatusModel? status,
int? scheduledHours,
int? scheduledMinutes,
bool? isTimerActive,
int? remainingTime,
}) {
return ACStatusLoaded(
status: status ?? this.status,
scheduledHours: scheduledHours ?? this.scheduledHours,
scheduledMinutes: scheduledMinutes ?? this.scheduledMinutes,
isTimerActive: isTimerActive ?? this.isTimerActive,
);
}
@override
List<Object> get props => [status, timestamp];
@ -40,3 +63,14 @@ class AcsFailedState extends AcsState {
@override
List<Object> get props => [error];
}
class TimerRunInProgress extends AcsState {
final int remainingTime;
const TimerRunInProgress(this.remainingTime);
@override
List<Object> get props => [remainingTime];
}

View File

@ -11,6 +11,7 @@ class AcStatusModel {
final bool childLock;
final TempModes acMode;
final FanSpeeds acFanSpeed;
final int countdown1;
AcStatusModel({
required this.uuid,
@ -18,6 +19,7 @@ class AcStatusModel {
required this.modeString,
required this.tempSet,
required this.currentTemp,
required this.countdown1,
required this.fanSpeedsString,
required this.childLock,
}) : acMode = getACMode(modeString),
@ -30,6 +32,7 @@ class AcStatusModel {
late int currentTemp;
late String fanSpeeds;
late bool childLock;
late int _countdown1 = 0;
for (var status in jsonList) {
switch (status.code) {
@ -51,6 +54,9 @@ class AcStatusModel {
case 'child_lock':
childLock = status.value ?? false;
break;
case 'countdown_time':
_countdown1 = status.value ?? 0;
break;
}
}
@ -62,6 +68,7 @@ class AcStatusModel {
currentTemp: currentTemp,
fanSpeedsString: fanSpeeds,
childLock: childLock,
countdown1: _countdown1,
);
}
@ -73,6 +80,7 @@ class AcStatusModel {
int? currentTemp,
String? fanSpeedsString,
bool? childLock,
int? countdown1,
}) {
return AcStatusModel(
uuid: uuid ?? this.uuid,
@ -82,6 +90,7 @@ class AcStatusModel {
currentTemp: currentTemp ?? this.currentTemp,
fanSpeedsString: fanSpeedsString ?? this.fanSpeedsString,
childLock: childLock ?? this.childLock,
countdown1: countdown1 ?? this.countdown1,
);
}

View File

@ -10,11 +10,10 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
const AcDeviceControlsView({super.key, required this.device});
const AcDeviceControlsView({super.key, required this.device});
final AllDevicesModel device;
@ -23,11 +22,13 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => AcBloc(deviceId: device.uuid!)
..add(AcFetchDeviceStatusEvent(device.uuid!)),
child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) {
final acBloc = BlocProvider.of<AcBloc>(context);
if (state is ACStatusLoaded) {
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
@ -78,56 +79,101 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
),
ToggleWidget(
label: '',
labelWidget: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
labelWidget: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
padding: const EdgeInsets.all(0),
onPressed: () {},
icon: const Icon(
Icons.remove,
size: 28,
color: ColorsManager.greyColor,
Container(
width: MediaQuery.of(context).size.width,
decoration: const ShapeDecoration(
color: ColorsManager.primaryColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(30)),
),
),
),
Text(
'06',
style: context.textTheme.titleLarge!.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'h',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor),
),
Text(
'30',
style: context.textTheme.titleLarge!.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text('m',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor)),
IconButton(
padding: const EdgeInsets.all(0),
onPressed: () {},
icon: const Icon(
Icons.add,
size: 28,
color: ColorsManager.greyColor,
Center(
child: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconButton(
onPressed: () {
if (acBloc.timerActive == false) {
context
.read<AcBloc>()
.add(DecreaseTimeEvent());
}
},
icon: const Icon(Icons.remove,
color: ColorsManager.greyColor),
),
Text(
acBloc.scheduledHours
.toString()
.padLeft(2, '0'),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'h',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
Text(
acBloc.scheduledMinutes
.toString()
.padLeft(2, '0'),
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(
color: ColorsManager.dialogBlueTitle,
fontWeight: FontWeight.bold,
),
),
Text(
'm',
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: ColorsManager.blackColor,
),
),
IconButton(
onPressed: () {
if (acBloc.timerActive == false) {
context
.read<AcBloc>()
.add(IncreaseTimeEvent());
}
},
icon: const Icon(Icons.add,
color: ColorsManager.greyColor),
),
],
),
),
),
],
),
value: false,
value: acBloc.timerActive,
code: 'ac_schedule',
deviceId: device.uuid!,
icon: Assets.acSchedule,
onChange: (value) {},
onChange: (value) {
context.read<AcBloc>().add(ToggleScheduleEvent());
},
),
ToggleWidget(
deviceId: device.uuid!,

View File

@ -60,7 +60,15 @@ class _CurrentTempState extends State<CurrentTemp> {
);
});
}
@override
void didUpdateWidget(CurrentTemp oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.tempSet != widget.tempSet) {
setState(() {
_adjustedValue = _initialAdjustedValue(widget.tempSet);
});
}
}
@override
void dispose() {
_debounce?.cancel();

View File

@ -9,6 +9,8 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_batch_control_view.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart';
@ -104,6 +106,9 @@ mixin RouteControlsBasedCode {
);
case 'SOS':
return SosDeviceControlsView(device: device);
case 'NCPS':
return FlushMountedPresenceSensorControlView(device: device);
default:
return const SizedBox();
}
@ -194,6 +199,10 @@ mixin RouteControlsBasedCode {
return SOSBatchControlView(
deviceIds: devices.where((e) => (e.productType == 'SOS')).map((e) => e.uuid!).toList(),
);
case 'NCPS':
return FlushMountedPresenceSensorBatchControlView(
devicesIds: devices.where((e) => (e.productType == 'NCPS')).map((e) => e.uuid!).toList(),
);
default:
return const SizedBox();
}

View File

@ -0,0 +1,18 @@
class DeviceSubSpace {
String? id;
String? createdAt;
String? updatedAt;
String? subspaceName;
bool? disabled;
DeviceSubSpace({this.id, this.createdAt, this.updatedAt, this.subspaceName, this.disabled});
DeviceSubSpace.fromJson(Map<String, dynamic> json) {
id = json['uuid']?.toString() ?? '';
createdAt = json['createdAt']?.toString() ?? '';
updatedAt = json['updatedAt']?.toString() ?? '';
subspaceName = json['subspaceName']?.toString() ?? '';
subspaceName = json['subspaceName']?.toString() ?? '';
disabled = json['disabled'] ?? false;
}
}

View File

@ -0,0 +1,20 @@
class DeviceTagModel {
String? id;
String? createdAt;
String? updatedAt;
String? name;
DeviceTagModel({
this.id,
this.createdAt,
this.updatedAt,
this.name,
});
DeviceTagModel.fromJson(Map<String, dynamic> json) {
id = json['uuid']?.toString() ?? '';
createdAt = json['createdAt']?.toString() ?? '';
updatedAt = json['updatedAt']?.toString() ?? '';
name = json['name']?.toString() ?? '';
}
}

View File

@ -1,6 +1,8 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sub_space.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_tag_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
@ -10,6 +12,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switc
import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/pages/routines/models/gateway.dart';
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart';
@ -78,38 +81,41 @@ class AllDevicesModel {
int? batteryLevel;
String? productName;
List<DeviceSpaceModel>? spaces;
List<DeviceTagModel>? deviceTags;
DeviceSubSpace? deviceSubSpace;
AllDevicesModel({
this.room,
this.subspace,
this.unit,
this.community,
this.productUuid,
this.productType,
this.permissionType,
this.activeTime,
this.category,
this.categoryName,
this.createTime,
this.gatewayId,
this.icon,
this.ip,
this.lat,
this.localKey,
this.lon,
this.model,
this.name,
this.nodeId,
this.online,
this.ownerId,
this.sub,
this.timeZone,
this.updateTime,
this.uuid,
this.batteryLevel,
this.productName,
this.spaces,
});
AllDevicesModel(
{this.room,
this.subspace,
this.unit,
this.community,
this.productUuid,
this.productType,
this.permissionType,
this.activeTime,
this.category,
this.categoryName,
this.createTime,
this.gatewayId,
this.icon,
this.ip,
this.lat,
this.localKey,
this.lon,
this.model,
this.name,
this.nodeId,
this.online,
this.ownerId,
this.sub,
this.timeZone,
this.updateTime,
this.uuid,
this.batteryLevel,
this.productName,
this.spaces,
this.deviceTags,
this.deviceSubSpace});
AllDevicesModel.fromJson(Map<String, dynamic> json) {
room = (json['room'] != null && (json['room'] is Map))
@ -147,12 +153,15 @@ class AllDevicesModel {
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
uuid = json['uuid']?.toString();
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
productName = json['productName']?.toString();
deviceTags = json['deviceTag'] != null && json['deviceTag'] is List
? (json['deviceTag'] as List).map((tag) => DeviceTagModel.fromJson(tag)).toList()
: [];
deviceSubSpace = json['subspace'] != null
? DeviceSubSpace.fromJson(json['subspace'])
: DeviceSubSpace(subspaceName: '');
if (json['spaces'] != null && json['spaces'] is List) {
spaces = (json['spaces'] as List)
.map((space) => DeviceSpaceModel.fromJson(space))
.toList();
spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
}
}
@ -200,8 +209,7 @@ SOS
String tempIcon = '';
if (type == DeviceType.LightBulb) {
tempIcon = Assets.lightBulb;
} else if (type == DeviceType.CeilingSensor ||
type == DeviceType.WallSensor) {
} else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
tempIcon = Assets.sensors;
} else if (type == DeviceType.AC) {
tempIcon = Assets.ac;
@ -231,8 +239,6 @@ SOS
// tempIcon = Assets.gang3touch;
} else if (type == DeviceType.WaterLeak) {
tempIcon = Assets.waterLeakNormal;
} else if (type == DeviceType.WaterLeak) {
tempIcon = Assets.waterLeakNormal;
} else {
tempIcon = Assets.logoHorizontal;
}
@ -248,72 +254,51 @@ SOS
switch (productType) {
case 'AC':
return [
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
];
case '1G':
return [
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction(
deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '2G':
return [
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
];
case '3G':
return [
ThreeGangSwitch1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
];
case 'WPS':
return [
//IF Functions
PresenceStateFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
CurrentDistanceFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
IlluminanceValueFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceStateFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
CurrentDistanceFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
IlluminanceValueFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
PresenceTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
//THEN Functions
FarDetectionFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionSensitivityFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionLessSensitivityFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
IndicatorFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
NoOneTimeFunction(
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
// FarDetectionSliderFunction(
// deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN')
FarDetectionFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
MotionLessSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
IndicatorFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
NoOneTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
];
case 'GW':
return [
@ -333,6 +318,11 @@ SOS
type: 'BOTH',
),
];
case 'CPS':
return CeilingSensorHelper.getCeilingSensorFunctions(
uuid: uuid ?? '',
name: name ?? '',
);
default:
return [];
}

View File

@ -19,6 +19,7 @@ class FactoryResetModel {
Map<String, dynamic> toJson() {
return {
'devicesUuid': devicesUuid,
'operationType': operationType,
};
}
@ -33,6 +34,7 @@ class FactoryResetModel {
Map<String, dynamic> toMap() {
return {
'devicesUuid': devicesUuid,
'operationType': operationType,
};
}
@ -56,3 +58,4 @@ class FactoryResetModel {
@override
int get hashCode => devicesUuid.hashCode;
}

View File

@ -108,7 +108,7 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return DeviceManagementBody(
devices: deviceState.filteredDevices);
} else {
return const Center(child: Text('Error fetching Devices'));
return const DeviceManagementBody(devices: []);
}
},
);

View File

@ -72,6 +72,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: state is DeviceManagementLoading
? const Center(child: CircularProgressIndicator())
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: isLargeScreenSize(context)

View File

@ -14,29 +14,29 @@ class DeviceSearchFilters extends StatefulWidget {
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout {
final _unitNameController = TextEditingController();
final _productNameController = TextEditingController();
late final TextEditingController _unitNameController;
late final TextEditingController _productNameController;
List<Widget> get _widgets => [
_buildSearchField("Space Name", _unitNameController, 200),
_buildSearchField("Device Name / Product Name", _productNameController, 300),
_buildSearchResetButtons(),
];
@override
void initState() {
_unitNameController = TextEditingController();
_productNameController = TextEditingController();
super.initState();
}
@override
Widget build(BuildContext context) {
if (isExtraLargeScreenSize(context)) {
return Row(
children: _widgets
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
.toList(),
);
}
return Wrap(
alignment: WrapAlignment.start,
runAlignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
spacing: 20,
runSpacing: 10,
children: _widgets,
children: [
_buildSearchField("Space Name", _unitNameController, 200),
_buildSearchField("Device Name / Product Name", _productNameController, 300),
_buildSearchResetButtons(),
],
);
}

View File

@ -16,7 +16,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dar
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
class CeilingSensorControlsView extends StatelessWidget
with HelperResponsiveLayout {
const CeilingSensorControlsView({super.key, required this.device});
final AllDevicesModel device;
@ -31,29 +32,35 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
..add(CeilingInitialEvent(device.uuid ?? '')),
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
builder: (context, state) {
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
if (state is CeilingLoadingInitialState ||
state is CeilingReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is CeilingUpdateState) {
return _buildGridView(
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
return _buildGridView(context, state.ceilingSensorModel,
isExtraLarge, isLarge, isMedium);
} else if (state is CeilingReportsState) {
return ReportsTable(
report: state.deviceReport,
onRowTap: (index) {},
onClose: () {
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
context
.read<CeilingSensorBloc>()
.add(BackToCeilingGridViewEvent());
},
);
} else if (state is ShowCeilingDescriptionState) {
return DescriptionView(
description: state.description,
onClose: () {
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
context
.read<CeilingSensorBloc>()
.add(BackToCeilingGridViewEvent());
},
);
} else if (state is CeilingReportsFailedState) {
final model = context.read<CeilingSensorBloc>().deviceStatus;
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
return _buildGridView(
context, model, isExtraLarge, isLarge, isMedium);
}
return const Center(child: Text('Error fetching status'));
},
@ -61,8 +68,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
);
}
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
bool isLarge, bool isMedium) {
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
bool isExtraLarge, bool isLarge, bool isMedium) {
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true,
@ -143,8 +150,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
),
GestureDetector(
onTap: () {
context.read<CeilingSensorBloc>().add(
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
code: 'presence_state', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.illuminanceRecordIcon,
@ -153,9 +160,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
),
GestureDetector(
onTap: () {
context
.read<CeilingSensorBloc>()
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
code: '', deviceUuid: device.uuid!));
},
child: const PresenceStaticWidget(
icon: Assets.helpDescriptionIcon,

View File

@ -0,0 +1,250 @@
import 'dart:async';
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
part 'flush_mounted_presence_sensor_event.dart';
part 'flush_mounted_presence_sensor_state.dart';
class FlushMountedPresenceSensorBloc
extends Bloc<FlushMountedPresenceSensorEvent, FlushMountedPresenceSensorState> {
final String deviceId;
final ControlDeviceService controlDeviceService;
final BatchControlDevicesService batchControlDevicesService;
late FlushMountedPresenceSensorModel deviceStatus;
FlushMountedPresenceSensorBloc({
required this.deviceId,
required this.controlDeviceService,
required this.batchControlDevicesService,
}) : super(FlushMountedPresenceSensorInitialState()) {
on<FlushMountedPresenceSensorFetchStatusEvent>(
_onFlushMountedPresenceSensorFetchStatusEvent,
);
on<FlushMountedPresenceSensorFetchBatchStatusEvent>(
_onFlushMountedPresenceSensorFetchBatchStatusEvent);
on<FlushMountedPresenceSensorChangeValueEvent>(
_onFlushMountedPresenceSensorChangeValueEvent,
);
on<FlushMountedPresenceSensorBatchControlEvent>(
_onFlushMountedPresenceSensorBatchControlEvent,
);
on<FlushMountedPresenceSensorGetDeviceReportsEvent>(
_onFlushMountedPresenceSensorGetDeviceReportsEvent);
on<FlushMountedPresenceSensorShowDescriptionEvent>(
_onFlushMountedPresenceSensorShowDescriptionEvent,
);
on<FlushMountedPresenceSensorBackToGridViewEvent>(
_onFlushMountedPresenceSensorBackToGridViewEvent,
);
on<FlushMountedPresenceSensorFactoryResetEvent>(
_onFlushMountedPresenceSensorFactoryResetEvent,
);
on<FlushMountedPresenceSensorStatusUpdatedEvent>(
_onFlushMountedPresenceSensorStatusUpdatedEvent,
);
}
void _onFlushMountedPresenceSensorFetchStatusEvent(
FlushMountedPresenceSensorFetchStatusEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorLoadingInitialState());
try {
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
_listenToChanges(deviceId);
} catch (e) {
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
return;
}
}
Future<void> _onFlushMountedPresenceSensorFetchBatchStatusEvent(
FlushMountedPresenceSensorFetchBatchStatusEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorLoadingInitialState());
try {
final response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
} catch (e) {
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
}
}
void _listenToChanges(String deviceId) {
try {
final ref = FirebaseDatabase.instance.ref(
'device-status/$deviceId',
);
ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
eventsMap['status'].forEach((element) {
statusList.add(
Status(code: element['code'], value: element['value']),
);
});
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
if (!isClosed) {
add(FlushMountedPresenceSensorStatusUpdatedEvent(deviceStatus));
}
});
} catch (_) {
log(
'Error listening to changes',
name: 'FlushMountedPresenceSensorBloc._listenToChanges',
);
}
}
void _onFlushMountedPresenceSensorChangeValueEvent(
FlushMountedPresenceSensorChangeValueEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
_updateDeviceFunctionFromCode(event.code, event.value);
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
try {
await controlDeviceService.controlDevice(
deviceUuid: deviceId,
status: Status(code: event.code, value: event.value),
);
} catch (_) {
await _reloadDeviceStatus();
}
}
Future<void> _onFlushMountedPresenceSensorBatchControlEvent(
FlushMountedPresenceSensorBatchControlEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
_updateDeviceFunctionFromCode(event.code, event.value);
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
try {
await batchControlDevicesService.batchControlDevices(
uuids: event.deviceIds,
code: event.code,
value: event.value,
);
} catch (_) {
await _reloadDeviceStatus();
}
}
void _updateDeviceFunctionFromCode(String code, int value) {
switch (code) {
case FlushMountedPresenceSensorModel.codeFarDetection:
deviceStatus.farDetection = value;
break;
case FlushMountedPresenceSensorModel.codeSensitivity:
deviceStatus.sensitivity = value;
break;
case FlushMountedPresenceSensorModel.codeNoneDelay:
deviceStatus.noneDelay = value;
break;
case FlushMountedPresenceSensorModel.codePresenceDelay:
deviceStatus.presenceDelay = value;
break;
case FlushMountedPresenceSensorModel.codeNearDetection:
deviceStatus.nearDetection = value;
break;
case FlushMountedPresenceSensorModel.codeOccurDistReduce:
deviceStatus.occurDistReduce = value;
break;
case FlushMountedPresenceSensorModel.codeSensiReduce:
deviceStatus.sensiReduce = value;
break;
default:
return;
}
}
Future<void> _reloadDeviceStatus() async {
await Future.delayed(const Duration(milliseconds: 500), () {
add(FlushMountedPresenceSensorFetchStatusEvent());
});
}
Future<void> _onFlushMountedPresenceSensorGetDeviceReportsEvent(
FlushMountedPresenceSensorGetDeviceReportsEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorDeviceReportsLoadingState());
try {
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(FlushMountedPresenceSensorDeviceReportsState(
deviceReport: value, code: event.code));
});
} catch (e) {
emit(FlushMountedPresenceSensorDeviceReportsFailedState(error: e.toString()));
return;
}
}
void _onFlushMountedPresenceSensorShowDescriptionEvent(
FlushMountedPresenceSensorShowDescriptionEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) {
emit(FlushMountedPresenceSensorShowDescriptionState(
description: event.description));
}
void _onFlushMountedPresenceSensorBackToGridViewEvent(
FlushMountedPresenceSensorBackToGridViewEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) {
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
}
Future<void> _onFlushMountedPresenceSensorFactoryResetEvent(
FlushMountedPresenceSensorFactoryResetEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) async {
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
try {
final response = await DevicesManagementApi().factoryReset(
event.factoryReset,
event.deviceId,
);
if (!response) {
emit(
const FlushMountedPresenceSensorFailedState(
error: 'Something went wrong with factory reset, please try again',
),
);
} else {
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
}
} catch (e) {
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
}
}
void _onFlushMountedPresenceSensorStatusUpdatedEvent(
FlushMountedPresenceSensorStatusUpdatedEvent event,
Emitter<FlushMountedPresenceSensorState> emit,
) {
deviceStatus = event.model;
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
}
}

View File

@ -0,0 +1,93 @@
part of 'flush_mounted_presence_sensor_bloc.dart';
sealed class FlushMountedPresenceSensorEvent extends Equatable {
const FlushMountedPresenceSensorEvent();
@override
List<Object> get props => [];
}
class FlushMountedPresenceSensorFetchStatusEvent
extends FlushMountedPresenceSensorEvent {}
class FlushMountedPresenceSensorStatusUpdatedEvent
extends FlushMountedPresenceSensorEvent {
const FlushMountedPresenceSensorStatusUpdatedEvent(this.model);
final FlushMountedPresenceSensorModel model;
@override
List<Object> get props => [model];
}
class FlushMountedPresenceSensorChangeValueEvent
extends FlushMountedPresenceSensorEvent {
final int value;
final String code;
const FlushMountedPresenceSensorChangeValueEvent({
required this.value,
required this.code,
});
@override
List<Object> get props => [value, code];
}
class FlushMountedPresenceSensorFetchBatchStatusEvent
extends FlushMountedPresenceSensorEvent {
final List<String> devicesIds;
const FlushMountedPresenceSensorFetchBatchStatusEvent(this.devicesIds);
@override
List<Object> get props => [devicesIds];
}
class FlushMountedPresenceSensorGetDeviceReportsEvent
extends FlushMountedPresenceSensorEvent {
final String deviceUuid;
final String code;
const FlushMountedPresenceSensorGetDeviceReportsEvent({
required this.deviceUuid,
required this.code,
});
@override
List<Object> get props => [deviceUuid, code];
}
class FlushMountedPresenceSensorShowDescriptionEvent
extends FlushMountedPresenceSensorEvent {
final String description;
const FlushMountedPresenceSensorShowDescriptionEvent({required this.description});
}
class FlushMountedPresenceSensorBackToGridViewEvent
extends FlushMountedPresenceSensorEvent {}
class FlushMountedPresenceSensorBatchControlEvent
extends FlushMountedPresenceSensorEvent {
final List<String> deviceIds;
final String code;
final dynamic value;
const FlushMountedPresenceSensorBatchControlEvent({
required this.deviceIds,
required this.code,
required this.value,
});
@override
List<Object> get props => [deviceIds, code, value];
}
class FlushMountedPresenceSensorFactoryResetEvent
extends FlushMountedPresenceSensorEvent {
final String deviceId;
final FactoryResetModel factoryReset;
const FlushMountedPresenceSensorFactoryResetEvent({
required this.deviceId,
required this.factoryReset,
});
}

View File

@ -0,0 +1,76 @@
part of 'flush_mounted_presence_sensor_bloc.dart';
sealed class FlushMountedPresenceSensorState extends Equatable {
const FlushMountedPresenceSensorState();
@override
List<Object> get props => [];
}
class FlushMountedPresenceSensorInitialState
extends FlushMountedPresenceSensorState {}
class FlushMountedPresenceSensorLoadingInitialState
extends FlushMountedPresenceSensorState {}
class FlushMountedPresenceSensorUpdateState extends FlushMountedPresenceSensorState {
final FlushMountedPresenceSensorModel model;
const FlushMountedPresenceSensorUpdateState({required this.model});
@override
List<Object> get props => [model];
}
class FlushMountedPresenceSensorLoadingNewSate
extends FlushMountedPresenceSensorState {
final FlushMountedPresenceSensorModel model;
const FlushMountedPresenceSensorLoadingNewSate({required this.model});
@override
List<Object> get props => [model];
}
class FlushMountedPresenceSensorFailedState extends FlushMountedPresenceSensorState {
final String error;
const FlushMountedPresenceSensorFailedState({required this.error});
@override
List<Object> get props => [error];
}
class FlushMountedPresenceSensorDeviceReportsLoadingState
extends FlushMountedPresenceSensorState {}
class FlushMountedPresenceSensorDeviceReportsState
extends FlushMountedPresenceSensorState {
const FlushMountedPresenceSensorDeviceReportsState({
required this.deviceReport,
required this.code,
});
final DeviceReport deviceReport;
final String code;
@override
List<Object> get props => [deviceReport, code];
}
class FlushMountedPresenceSensorDeviceReportsFailedState
extends FlushMountedPresenceSensorState {
const FlushMountedPresenceSensorDeviceReportsFailedState({required this.error});
final String error;
@override
List<Object> get props => [error];
}
class FlushMountedPresenceSensorShowDescriptionState
extends FlushMountedPresenceSensorState {
const FlushMountedPresenceSensorShowDescriptionState({required this.description});
final String description;
@override
List<Object> get props => [description];
}

View File

@ -0,0 +1,21 @@
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart';
abstract final class FlushMountedPresenceSensorBlocFactory {
const FlushMountedPresenceSensorBlocFactory._();
static FlushMountedPresenceSensorBloc create({
required String deviceId,
}) {
return FlushMountedPresenceSensorBloc(
deviceId: deviceId,
controlDeviceService: DebouncedControlDeviceService(
decoratee: RemoteControlDeviceService(),
),
batchControlDevicesService: DebouncedBatchControlDevicesService(
decoratee: RemoteBatchControlDevicesService(),
),
);
}
}

View File

@ -0,0 +1,99 @@
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
class FlushMountedPresenceSensorModel {
FlushMountedPresenceSensorModel({
required this.presenceState,
required this.farDetection,
required this.illuminance,
required this.sensitivity,
required this.occurDistReduce,
required this.noneDelay,
required this.presenceDelay,
required this.nearDetection,
required this.sensiReduce,
required this.checkingResult,
});
static const String codePresenceState = 'presence_state';
static const String codeSensitivity = 'sensitivity';
static const String codeNearDetection = 'near_detection';
static const String codeFarDetection = 'far_detection';
static const String codeCheckingResult = 'checking_result';
static const String codePresenceDelay = 'presence_delay';
static const String codeNoneDelay = 'none_delay';
static const String codeOccurDistReduce = 'occur_dist_reduce';
static const String codeIlluminance = 'illum_value';
static const String codeSensiReduce = 'sensi_reduce';
String presenceState;
int sensitivity;
int nearDetection;
int farDetection;
String checkingResult;
int presenceDelay;
int noneDelay;
int occurDistReduce;
int illuminance;
int sensiReduce;
factory FlushMountedPresenceSensorModel.fromJson(List<Status> jsonList) {
String presenceState = 'none';
int sensitivity = 0;
int nearDetection = 0;
int farDetection = 0;
String checkingResult = 'none';
int presenceDelay = 0;
int noneDelay = 0;
int occurDistReduce = 0;
int illuminance = 0;
int sensiReduce = 0;
for (var status in jsonList) {
switch (status.code) {
case codePresenceState:
presenceState = status.value ?? 'presence';
break;
case codeSensitivity:
sensitivity = status.value ?? 0;
break;
case codeNearDetection:
nearDetection = status.value ?? 0;
break;
case codeFarDetection:
farDetection = status.value ?? 0;
break;
case codeCheckingResult:
checkingResult = status.value ?? 'check_success';
break;
case codePresenceDelay:
presenceDelay = status.value ?? 0;
break;
case codeNoneDelay:
noneDelay = status.value ?? 0;
break;
case codeOccurDistReduce:
occurDistReduce = status.value ?? 0;
break;
case codeIlluminance:
illuminance = status.value ?? 0;
break;
case codeSensiReduce:
sensiReduce = status.value ?? 0;
break;
}
}
return FlushMountedPresenceSensorModel(
presenceState: presenceState,
sensitivity: sensitivity,
nearDetection: nearDetection,
farDetection: farDetection,
checkingResult: checkingResult,
presenceDelay: presenceDelay,
noneDelay: noneDelay,
occurDistReduce: occurDistReduce,
illuminance: illuminance,
sensiReduce: sensiReduce,
);
}
}

View File

@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
with HelperResponsiveLayout {
const FlushMountedPresenceSensorBatchControlView({
required this.devicesIds,
super.key,
});
final List<String> devicesIds;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
deviceId: devicesIds.first,
)..add(FlushMountedPresenceSensorFetchBatchStatusEvent(devicesIds)),
child: BlocBuilder<FlushMountedPresenceSensorBloc,
FlushMountedPresenceSensorState>(
builder: (context, state) {
if (state is FlushMountedPresenceSensorLoadingInitialState ||
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is FlushMountedPresenceSensorUpdateState) {
return _buildGridView(context, state.model);
}
return const Center(child: Text('Error fetching status'));
},
),
);
}
Widget _buildGridView(
BuildContext context,
FlushMountedPresenceSensorModel model,
) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: isLarge || isExtraLarge
? 3
: isMedium
? 2
: 1,
mainAxisExtent: 140,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
children: [
PresenceUpdateData(
value: model.sensitivity.toDouble(),
title: 'Sensitivity:',
minValue: 0,
maxValue: 9,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeSensitivity,
value: value,
),
),
),
PresenceUpdateData(
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
title: 'Nearest Detect Dist:',
description: 'm',
minValue: 0.0,
maxValue: 9.5,
steps: 0.1,
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeNearDetection,
value: (value * 100).toInt(),
),
),
),
PresenceUpdateData(
value: (model.farDetection / 100).clamp(0.0, double.infinity),
title: 'Max Detect Dist:',
description: 'm',
minValue: 0.0,
maxValue: 9.5,
steps: 0.1,
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeFarDetection,
value: (value * 100).toInt(),
),
),
),
PresenceUpdateData(
value: model.sensiReduce.toDouble(),
title: 'Trigger Level:',
minValue: 0,
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeSensiReduce,
value: value,
),
),
),
PresenceUpdateData(
value: model.occurDistReduce.toDouble(),
title: 'Indent Level:',
minValue: 0,
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
value: value,
),
),
),
PresenceUpdateData(
value: (model.presenceDelay / 10).toDouble(),
title: 'Target Confirm Time:',
description: 's',
minValue: 0.0,
maxValue: 0.5,
steps: 0.1,
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codePresenceDelay,
value: (value * 10).toInt(),
),
),
),
PresenceUpdateData(
value: ((model.noneDelay / 10).toDouble()),
description: 's',
title: 'Disappe Delay:',
minValue: 20,
maxValue: 300,
steps: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorBatchControlEvent(
deviceIds: devicesIds,
code: FlushMountedPresenceSensorModel.codeNoneDelay,
value: (value * 10).round(),
),
),
),
FactoryResetWidget(
callFactoryReset: () {
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorFactoryResetEvent(
deviceId: devicesIds.first,
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
),
);
},
),
],
);
}
}

View File

@ -0,0 +1,219 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart';
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
import 'package:syncrow_web/pages/device_managment/shared/table/description_view.dart';
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class FlushMountedPresenceSensorControlView extends StatelessWidget
with HelperResponsiveLayout {
const FlushMountedPresenceSensorControlView({required this.device, super.key});
final AllDevicesModel device;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
deviceId: device.uuid ?? '-1',
)..add(FlushMountedPresenceSensorFetchStatusEvent()),
child: BlocBuilder<FlushMountedPresenceSensorBloc,
FlushMountedPresenceSensorState>(
builder: (context, state) {
if (state is FlushMountedPresenceSensorLoadingInitialState ||
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
return const Center(child: CircularProgressIndicator());
} else if (state is FlushMountedPresenceSensorUpdateState) {
return _buildGridView(context, state.model);
} else if (state is FlushMountedPresenceSensorDeviceReportsState) {
return ReportsTable(
report: state.deviceReport,
thirdColumnTitle:
state.code == 'illuminance_value' ? "Value" : 'Status',
thirdColumnDescription:
state.code == 'illuminance_value' ? "Lux" : null,
onRowTap: (index) {},
onClose: () {
context
.read<FlushMountedPresenceSensorBloc>()
.add(FlushMountedPresenceSensorBackToGridViewEvent());
},
);
} else if (state is FlushMountedPresenceSensorShowDescriptionState) {
return DescriptionView(
description: state.description,
onClose: () {
context
.read<FlushMountedPresenceSensorBloc>()
.add(FlushMountedPresenceSensorBackToGridViewEvent());
},
);
} else if (state is FlushMountedPresenceSensorDeviceReportsFailedState) {
final model =
context.read<FlushMountedPresenceSensorBloc>().deviceStatus;
return _buildGridView(context, model);
}
return const Center(
child: Text('Error fetching status', textAlign: TextAlign.center),
);
},
),
);
}
Widget _buildGridView(
BuildContext context,
FlushMountedPresenceSensorModel model,
) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return GridView(
padding: const EdgeInsets.symmetric(horizontal: 50),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: isLarge || isExtraLarge
? 3
: isMedium
? 2
: 1,
mainAxisExtent: 140,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
children: [
PresenceState(
value: model.presenceState,
),
PresenceDisplayValue(
value: model.illuminance.toString(),
postfix: 'Lux',
description: 'Illuminance Value',
),
PresenceUpdateData(
value: model.sensitivity.toDouble(),
title: 'Sensitivity:',
minValue: 0,
maxValue: 9,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeSensitivity,
value: value,
),
),
),
PresenceUpdateData(
value: (model.nearDetection / 100).clamp(0.0, double.infinity),
title: 'Nearest Detect Dist:',
description: 'm',
minValue: 0.0,
maxValue: 9.5,
steps: 0.1,
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeNearDetection,
value: (value * 100).toInt(),
),
),
),
PresenceUpdateData(
value: (model.farDetection / 100).clamp(0.0, double.infinity),
title: 'Max Detect Dist:',
description: 'm',
minValue: 0.0,
maxValue: 9.5,
steps: 0.1,
valuesPercision: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeFarDetection,
value: (value * 100).toInt(),
),
),
),
PresenceUpdateData(
value: model.sensiReduce.toDouble(),
title: 'Trigger Level:',
minValue: 0,
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeSensiReduce,
value: value,
),
),
),
PresenceUpdateData(
value: model.occurDistReduce.toDouble(),
title: 'Indent Level:',
minValue: 0,
maxValue: 3,
steps: 1,
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
value: value,
),
),
),
PresenceUpdateData(
value: (model.presenceDelay / 10).toDouble(),
valuesPercision: 1,
title: 'Target Confirm Time:',
description: 's',
minValue: 0.0,
maxValue: 0.5,
steps: 0.1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codePresenceDelay,
value: (value * 10).toInt(),
),
),
),
PresenceUpdateData(
value: (model.noneDelay / 10).toDouble(),
description: 's',
title: 'Disappe Delay:',
minValue: 20,
maxValue: 300,
steps: 1,
action: (double value) =>
context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorChangeValueEvent(
code: FlushMountedPresenceSensorModel.codeNoneDelay,
value: (value * 10).round(),
),
),
),
GestureDetector(
onTap: () => context.read<FlushMountedPresenceSensorBloc>().add(
FlushMountedPresenceSensorGetDeviceReportsEvent(
code: 'presence_state',
deviceUuid: device.uuid!,
),
),
child: const PresenceStaticWidget(
icon: Assets.presenceRecordIcon,
description: 'Presence Record',
),
),
],
);
}
}

View File

@ -14,27 +14,36 @@ class GatewayModel {
});
factory GatewayModel.fromJson(Map<String, dynamic> json) {
final status = json['status'] as List<dynamic>;
final status = json['status'] as List<dynamic>? ?? <dynamic>[];
final switchAlarmSound = status.firstWhere(
(item) => item['code'] == 'switch_alarm_sound',
)['value'] as bool;
final masterState = status.firstWhere(
(item) => item['code'] == 'master_state',
)['value'] as String;
final factoryReset = status.firstWhere(
(item) => item['code'] == 'factory_reset',
)['value'] as bool;
final alarmActive = status.firstWhere(
(item) => item['code'] == 'alarm_active',
)['value'] as String;
bool? switchAlarmSound;
String? masterState;
bool? factoryReset;
String? alarmActive;
for (final item in status) {
switch (item['code']) {
case 'switch_alarm_sound':
switchAlarmSound = item['value'] as bool;
break;
case 'master_state':
masterState = item['value'] as String;
break;
case 'factory_reset':
factoryReset = item['value'] as bool;
break;
case 'alarm_active':
alarmActive = item['value'] as String;
break;
}
}
return GatewayModel(
uuid: json['uuid'] as String,
switchAlarmSound: switchAlarmSound,
masterState: masterState,
factoryReset: factoryReset,
alarmActive: alarmActive,
uuid: json['uuid'] as String? ?? '',
switchAlarmSound: switchAlarmSound ?? false,
masterState: masterState ?? '',
factoryReset: factoryReset ?? false,
alarmActive: alarmActive ?? '',
);
}
}

View File

@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart';
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart';
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
// import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
class MainDoorSensorBatchView extends StatelessWidget {
const MainDoorSensorBatchView({super.key, required this.devicesIds});
@ -13,35 +12,31 @@ class MainDoorSensorBatchView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// SizedBox(
// width: 170,
// height: 140,
// child: FirmwareUpdateWidget(
// deviceId: devicesIds.first,
// version: 12,
// ),
// ),
// const SizedBox(
// width: 12,
// ),
SizedBox(
width: 170,
height: 140,
child: FactoryResetWidget(
callFactoryReset: () {
BlocProvider.of<MainDoorSensorBloc>(context).add(
MainDoorSensorFactoryReset(
deviceId: devicesIds.first,
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
return BlocProvider(
create: (context) => MainDoorSensorBloc(),
child: Builder(
builder: (innerContext) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 170,
height: 140,
child: FactoryResetWidget(
callFactoryReset: () {
BlocProvider.of<MainDoorSensorBloc>(innerContext).add(
MainDoorSensorFactoryReset(
deviceId: devicesIds.first,
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
),
);
},
),
);
},
),
),
],
),
],
);
},
),
);
}
}

View File

@ -217,29 +217,31 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
try {
var status =
await DevicesManagementApi().getPowerClampInfo(event.deviceId);
deviceStatus = PowerClampModel.fromJson(status);
deviceStatus = PowerClampModel.fromJson(status as Map<String, Object?>? ??{});
final phaseADataPoints = deviceStatus.status.phaseA.dataPoints;
final phaseBDataPoints = deviceStatus.status.phaseB.dataPoints;
final phaseCDataPoints = deviceStatus.status.phaseC.dataPoints;
phaseData = [
{
'name': 'Phase A',
'voltage': '${deviceStatus.status.phaseA.dataPoints[0].value / 10} V',
'current': '${deviceStatus.status.phaseA.dataPoints[1].value / 10} A',
'activePower': '${deviceStatus.status.phaseA.dataPoints[2].value} W',
'powerFactor': '${deviceStatus.status.phaseA.dataPoints[3].value}',
'voltage': '${(phaseADataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${(phaseADataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${phaseADataPoints.elementAtOrNull(2)?.value??'N/A'} W',
'powerFactor': '${phaseADataPoints.elementAtOrNull(3)?.value??'N/A'}',
},
{
'name': 'Phase B',
'voltage': '${deviceStatus.status.phaseB.dataPoints[0].value / 10} V',
'current': '${deviceStatus.status.phaseB.dataPoints[1].value / 10} A',
'activePower': '${deviceStatus.status.phaseB.dataPoints[2].value} W',
'powerFactor': '${deviceStatus.status.phaseB.dataPoints[3].value}',
'voltage': '${(phaseBDataPoints .elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${(phaseBDataPoints .elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${phaseBDataPoints.elementAtOrNull(2)?.value??'N/A'} W',
'powerFactor': '${phaseBDataPoints.elementAtOrNull(3)?.value??'N/A'}',
},
{
'name': 'Phase C',
'voltage': '${deviceStatus.status.phaseC.dataPoints[0].value / 10} V',
'current': '${deviceStatus.status.phaseC.dataPoints[1].value / 10} A',
'activePower': '${deviceStatus.status.phaseC.dataPoints[2].value} W',
'powerFactor': '${deviceStatus.status.phaseC.dataPoints[3].value}',
'voltage': '${(phaseCDataPoints.elementAtOrNull(0)?.value as num? ?? 0) / 10} V',
'current': '${(phaseCDataPoints.elementAtOrNull(1)?.value as num? ?? 0) / 10} A',
'activePower': '${phaseCDataPoints.elementAtOrNull(2)?.value ?? 'N/A'} W',
'powerFactor': '${phaseCDataPoints.elementAtOrNull(3)?.value ?? 'N/A'}',
},
];
emit(GetDeviceStatus());
@ -785,7 +787,7 @@ class SmartPowerBloc extends Bloc<SmartPowerEvent, SmartPowerState> {
void selectDateRange() async {
DateTime startDate = dateTime!;
DateTime endDate = DateTime(startDate.year, startDate.month + 1, 1)
.subtract(Duration(days: 1));
.subtract(const Duration(days: 1));
String formattedEndDate = DateFormat('dd/MM/yyyy').format(endDate);
endChartDate = ' - $formattedEndDate';
}

View File

@ -12,9 +12,9 @@ class PowerClampModel {
factory PowerClampModel.fromJson(Map<String, dynamic> json) {
return PowerClampModel(
productUuid: json['productUuid'],
productType: json['productType'],
status: PowerStatus.fromJson(json['status']),
productUuid: json['productUuid'] as String? ?? '',
productType: json['productType'] as String? ?? '',
status: PowerStatus.fromJson(json['status'] as Map<String, dynamic>? ?? {}),
);
}
@ -26,7 +26,7 @@ class PowerClampModel {
return PowerClampModel(
productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType,
status: statusPower ?? this.status,
status: statusPower ?? status,
);
}
}
@ -46,12 +46,10 @@ class PowerStatus {
factory PowerStatus.fromJson(Map<String, dynamic> json) {
return PowerStatus(
phaseA: Phase.fromJson(json['phaseA']),
phaseB: Phase.fromJson(json['phaseB']),
phaseC: Phase.fromJson(json['phaseC']),
general: Phase.fromJson(json['general']
// List<DataPoint>.from(
// json['general'].map((x) => DataPoint.fromJson(x))),
phaseA: Phase.fromJson(json['phaseA']as List<dynamic>? ?? []),
phaseB: Phase.fromJson(json['phaseB']as List<dynamic>? ?? []),
phaseC: Phase.fromJson(json['phaseC']as List<dynamic>? ?? []),
general: Phase.fromJson(json['general']as List<dynamic>? ?? []
));
}
}
@ -69,30 +67,30 @@ class Phase {
}
class DataPoint {
dynamic code;
dynamic customName;
dynamic dpId;
dynamic time;
dynamic type;
dynamic value;
final String? code;
final String? customName;
final int? dpId;
final int? time;
final String? type;
final dynamic value;
DataPoint({
required this.code,
required this.customName,
required this.dpId,
required this.time,
required this.type,
required this.value,
this.code,
this.customName,
this.dpId,
this.time,
this.type,
this.value,
});
factory DataPoint.fromJson(Map<String, dynamic> json) {
return DataPoint(
code: json['code'],
customName: json['customName'],
dpId: json['dpId'],
time: json['time'],
type: json['type'],
value: json['value'],
code: json['code'] as String?,
customName: json['customName'] as String?,
dpId: json['dpId'] as int?,
time: json['time'] as int?,
type: json['type'] as String?,
value: json['value'] as dynamic,
);
}
}

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class EnergyConsumptionPage extends StatefulWidget {
@ -10,7 +10,8 @@ class EnergyConsumptionPage extends StatefulWidget {
final Widget widget;
final Function()? onTap;
EnergyConsumptionPage({
const EnergyConsumptionPage({
super.key,
required this.chartData,
required this.totalConsumption,
required this.date,
@ -91,11 +92,12 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
],
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(top: 10),
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.11,
height: MediaQuery.sizeOf(context).height * 0.09,
child: LineChart(
LineChartData(
lineTouchData: LineTouchData(
@ -151,7 +153,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: RotatedBox(
quarterTurns: -1,
child: Text(_chartData[index].time,
style: TextStyle(fontSize: 10)),
style: const TextStyle(fontSize: 10)),
),
);
}
@ -190,8 +192,8 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
spots: _chartData
.asMap()
.entries
.map((entry) => FlSpot(entry.key.toDouble(),
entry.value.consumption))
.map((entry) => FlSpot(
entry.key.toDouble(), entry.value.consumption))
.toList(),
isCurved: true,
color: ColorsManager.primaryColor.withOpacity(0.6),
@ -218,7 +220,7 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
borderData: FlBorderData(
show: false,
border: Border.all(
color: Color(0xff023DFE).withOpacity(0.7),
color: const Color(0xff023DFE).withOpacity(0.7),
width: 10,
),
),
@ -253,11 +255,9 @@ class _EnergyConsumptionPageState extends State<EnergyConsumptionPage> {
child: InkWell(
onTap: widget.onTap,
child: Center(
child: SizedBox(
child: Padding(
padding: const EdgeInsets.all(5),
child: Text(widget.date),
),
child: Padding(
padding: const EdgeInsets.all(5),
child: Text(widget.date),
),
),
),

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});
@ -25,27 +24,27 @@ class SmartPowerDeviceControl extends StatelessWidget
..add(SmartPowerFetchDeviceEvent(deviceId)),
child: BlocBuilder<SmartPowerBloc, SmartPowerState>(
builder: (context, state) {
final _blocProvider = BlocProvider.of<SmartPowerBloc>(context);
final blocProvider = BlocProvider.of<SmartPowerBloc>(context);
if (state is SmartPowerLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is FakeState) {
return _buildStatusControls(
currentPage: _blocProvider.currentPage,
currentPage: blocProvider.currentPage,
context: context,
blocProvider: _blocProvider,
blocProvider: blocProvider,
);
} else if (state is GetDeviceStatus) {
return _buildStatusControls(
currentPage: _blocProvider.currentPage,
currentPage: blocProvider.currentPage,
context: context,
blocProvider: _blocProvider,
blocProvider: blocProvider,
);
} else if (state is FilterRecordsState) {
return _buildStatusControls(
currentPage: _blocProvider.currentPage,
currentPage: blocProvider.currentPage,
context: context,
blocProvider: _blocProvider,
blocProvider: blocProvider,
);
}
return const Center(child: CircularProgressIndicator());
@ -60,7 +59,7 @@ class SmartPowerDeviceControl extends StatelessWidget
required SmartPowerBloc blocProvider,
required int currentPage,
}) {
PageController _pageController = PageController(initialPage: currentPage);
PageController pageController = PageController(initialPage: currentPage);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 50),
child: DeviceControlsContainer(
@ -85,25 +84,31 @@ class SmartPowerDeviceControl extends StatelessWidget
PowerClampInfoCard(
iconPath: Assets.powerActiveIcon,
title: 'Active',
value: blocProvider
.deviceStatus.status.general.dataPoints[2].value
.toString(),
value: blocProvider.deviceStatus.status.general.dataPoints
.elementAtOrNull(2)
?.value
.toString() ??
'',
unit: '',
),
PowerClampInfoCard(
iconPath: Assets.voltMeterIcon,
title: 'Current',
value: blocProvider
.deviceStatus.status.general.dataPoints[1].value
.toString(),
value: blocProvider.deviceStatus.status.general.dataPoints
.elementAtOrNull(1)
?.value
.toString() ??
'',
unit: ' A',
),
PowerClampInfoCard(
iconPath: Assets.frequencyIcon,
title: 'Frequency',
value: blocProvider
.deviceStatus.status.general.dataPoints[4].value
.toString(),
value: blocProvider.deviceStatus.status.general.dataPoints
.elementAtOrNull(4)
?.value
.toString() ??
'',
unit: ' Hz',
),
],
@ -142,7 +147,7 @@ class SmartPowerDeviceControl extends StatelessWidget
icon: const Icon(Icons.arrow_left),
onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(-1));
_pageController.previousPage(
pageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
@ -162,7 +167,7 @@ class SmartPowerDeviceControl extends StatelessWidget
icon: const Icon(Icons.arrow_right),
onPressed: () {
blocProvider.add(SmartPowerArrowPressedEvent(1));
_pageController.nextPage(
pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
@ -177,7 +182,7 @@ class SmartPowerDeviceControl extends StatelessWidget
Expanded(
flex: 2,
child: PageView(
controller: _pageController,
controller: pageController,
onPageChanged: (int page) {
blocProvider.add(SmartPowerPageChangedEvent(page));
},
@ -190,8 +195,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

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class PresenceUpdateData extends StatefulWidget {
const PresenceUpdateData({
@ -13,6 +14,7 @@ class PresenceUpdateData extends StatefulWidget {
required this.maxValue,
required this.steps,
this.description,
this.valuesPercision = 0,
});
final String title;
@ -22,6 +24,7 @@ class PresenceUpdateData extends StatefulWidget {
final double steps;
final Function action;
final String? description;
final int valuesPercision;
@override
State<PresenceUpdateData> createState() => _CurrentTempState();
@ -45,7 +48,7 @@ class _CurrentTempState extends State<PresenceUpdateData> {
}
void _onValueChanged(double newValue) {
widget.action(newValue.toInt());
widget.action(newValue);
}
@override
@ -62,11 +65,14 @@ class _CurrentTempState extends State<PresenceUpdateData> {
children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 10),
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontWeight: FontWeight.w400,
fontSize: 10,
),
),
IncrementDecrementWidget(
value: widget.value.toString(),
value: widget.value.toStringAsFixed(widget.valuesPercision),
description: widget.description ?? '',
descriptionColor: ColorsManager.blackColor,
onIncrement: () {

View File

@ -84,6 +84,16 @@ class _PresenceUpdateDataState extends State<PresenceNoBodyTime> {
}
}
@override
void didUpdateWidget(PresenceNoBodyTime oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.value != widget.value) {
setState(() {
_currentValue = widget.value;
});
}
}
@override
Widget build(BuildContext context) {
return DeviceControlsContainer(

View File

@ -62,9 +62,6 @@ class ToggleWidget extends StatelessWidget {
)),
if (showToggle)
Container(
height: 20,
width: 35,
padding: const EdgeInsets.only(right: 16, top: 10),
child: CupertinoSwitch(
value: value,
activeColor: ColorsManager.dialogBlueTitle,

View File

@ -13,7 +13,8 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
import '../models/sos_status_model.dart';
class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
class SosDeviceControlsView extends StatelessWidget
with HelperResponsiveLayout {
const SosDeviceControlsView({
super.key,
required this.device,
@ -24,7 +25,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
create: (context) =>
SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
child: BlocBuilder<SosDeviceBloc, SosDeviceState>(
builder: (context, state) {
if (state is SosDeviceLoadingState) {
@ -63,7 +65,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
));
}
Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) {
Widget _buildStatusControls(
BuildContext context, SosStatusModel deviceStatus) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
@ -85,7 +88,7 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
IconNameStatusContainer(
isFullIcon: false,
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal,
icon: deviceStatus.sosStatus == 'sos' ? Assets.sosNormal : Assets.sos,
onTap: () {},
status: false,
textColor: ColorsManager.blackColor,

View File

@ -21,6 +21,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
on<ShowDescriptionEvent>(_showDescription);
on<BackToGridViewEvent>(_backToGridView);
on<WallSensorFactoryResetEvent>(_onFactoryReset);
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
}
void _fetchWallSensorStatus(
@ -30,7 +31,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
_listenToChanges(emit, deviceId);
_listenToChanges(deviceId);
} catch (e) {
emit(WallSensorFailedState(error: e.toString()));
return;
@ -52,28 +53,27 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
}
}
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
void _listenToChanges(String deviceId) {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
ref.onValue.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
final statusList = (data['status'] as List?)
?.map((e) => Status(code: e['code'], value: e['value']))
.toList();
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = WallSensorModel.fromJson(statusList);
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
});
} catch (_) {}
if (statusList != null) {
final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
if (!isClosed) {
add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
}
}
});
}
void _changeValue(
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
@ -195,4 +195,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
emit(WallSensorFailedState(error: e.toString()));
}
}
void _onRealtimeUpdate(
WallSensorRealtimeUpdateEvent event,
Emitter<WallSensorState> emit,
) {
deviceStatus = event.deviceStatus;
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
}
}

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
abstract class WallSensorEvent extends Equatable {
const WallSensorEvent();
@ -70,3 +71,8 @@ class WallSensorFactoryResetEvent extends WallSensorEvent {
required this.factoryReset,
});
}
class WallSensorRealtimeUpdateEvent extends WallSensorEvent {
final WallSensorModel deviceStatus;
const WallSensorRealtimeUpdateEvent(this.deviceStatus);
}

View File

@ -30,6 +30,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
condition: event.functionData.condition ?? existingData.condition,
);
} else {
functions.clear();
functions.add(event.functionData);
}

View File

@ -64,8 +64,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
TriggerSwitchTabsEvent event,
Emitter<RoutineState> emit,
) {
emit(state.copyWith(
routineTab: event.isRoutineTab, createRoutineView: false));
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
add(ResetRoutineState());
if (event.isRoutineTab) {
add(const LoadScenes());
@ -91,8 +90,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList
int index = updatedIfItems.indexWhere(
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
int index =
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid
if (index != -1) {
updatedIfItems[index] = event.item;
@ -101,21 +100,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
if (event.isTabToRun) {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else {
emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
}
}
void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// Find the index of the item in teh current itemsList
int index = currentItems.indexWhere(
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
int index =
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid
if (index != -1) {
currentItems[index] = event.item;
@ -126,45 +122,42 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems));
}
void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try {
if (event.functions.isEmpty) return;
List<DeviceFunctionData> selectedFunction =
List<DeviceFunctionData>.from(event.functions);
// List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(
currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode ==
currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode);
}
}
}
}
// if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
// List<DeviceFunctionData> currentFunctions =
// List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
for (int i = 0; i < functionCode.length; i++) {
selectedFunction
.removeWhere((code) => code.functionCode == functionCode[i]);
}
// List<String> functionCode = [];
// for (int i = 0; i < selectedFunction.length; i++) {
// for (int j = 0; j < currentFunctions.length; j++) {
// if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
// currentFunctions[j] = selectedFunction[i];
// if (!functionCode.contains(currentFunctions[j].functionCode)) {
// functionCode.add(currentFunctions[j].functionCode);
// }
// }
// }
// }
currentSelectedFunctions[event.uniqueCustomId] =
List.from(currentFunctions)..addAll(selectedFunction);
} else {
currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
}
// for (int i = 0; i < functionCode.length; i++) {
// selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
// }
// currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions)
// ..addAll(selectedFunction);
// } else {
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
// }
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
} catch (e) {
@ -172,30 +165,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
}
emit(state.copyWith(
@ -212,8 +199,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> automations = [];
final projectId = await ProjectManager.getProjectUUID() ?? '';
@ -221,23 +207,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
try {
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
automations.addAll(
await SceneApi.getAutomation(spaceId, communityId, projectId));
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
}
}
} else {
automations.addAll(await SceneApi.getAutomation(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectId));
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
}
emit(state.copyWith(
automations: automations,
@ -253,16 +233,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query));
}
FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon));
}
@ -276,8 +254,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.last['deviceId'] == 'delay';
}
Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) {
@ -290,8 +267,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
return;
@ -367,8 +343,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (state.routineName == null || state.routineName!.isEmpty) {
@ -390,8 +365,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
@ -482,8 +456,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions,
);
final result =
await SceneApi.createAutomation(createAutomationModel, projectUuid);
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
if (result['success']) {
add(ResetRoutineState());
add(const LoadAutomation());
@ -504,21 +477,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index);
selectedFunctions.remove(event.key);
emit(state.copyWith(
thenItems: thenItems, selectedFunctions: selectedFunctions));
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
} else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index);
selectedFunctions.remove(event.key);
@ -529,8 +498,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isAutomation: false,
isTabToRun: false));
} else {
emit(state.copyWith(
ifItems: ifItems, selectedFunctions: selectedFunctions));
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
}
}
}
@ -542,141 +510,138 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime));
}
FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith(
routineName: event.name,
));
}
(
List<Map<String, dynamic>>,
List<Map<String, dynamic>>,
Map<String, List<DeviceFunctionData>>
) _createCardData(
List<RoutineAction> actions,
List<RoutineCondition>? conditions,
Map<String, List<DeviceFunctionData>> currentFunctions,
bool isTabToRun,
) {
final ifItems = isTabToRun
? [
{
'entityId': 'tab_to_run',
'uniqueCustomId': const Uuid().v4(),
'deviceId': 'tab_to_run',
'title': 'Tab to run',
'productType': 'tab_to_run',
'imagePath': Assets.tabToRun,
}
]
: conditions?.map((condition) {
final matchingDevice = state.devices.firstWhere(
(device) => device.uuid == condition.entityId,
orElse: () => AllDevicesModel(
uuid: condition.entityId,
name: condition.entityId,
productType: condition.entityType,
),
);
// (
// List<Map<String, dynamic>>,
// List<Map<String, dynamic>>,
// Map<String, List<DeviceFunctionData>>
// ) _createCardData(
// List<RoutineAction> actions,
// List<RoutineCondition>? conditions,
// Map<String, List<DeviceFunctionData>> currentFunctions,
// bool isTabToRun,
// ) {
// final ifItems = isTabToRun
// ? [
// {
// 'entityId': 'tab_to_run',
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': 'tab_to_run',
// 'title': 'Tab to run',
// 'productType': 'tab_to_run',
// 'imagePath': Assets.tabToRun,
// }
// ]
// : conditions?.map((condition) {
// final matchingDevice = state.devices.firstWhere(
// (device) => device.uuid == condition.entityId,
// orElse: () => AllDevicesModel(
// uuid: condition.entityId,
// name: condition.entityId,
// productType: condition.entityType,
// ),
// );
final cardData = {
'entityId': condition.entityId,
'uniqueCustomId': const Uuid().v4(),
'deviceId': condition.entityId,
'title': matchingDevice.name ?? condition.entityId,
'productType': condition.entityType,
'imagePath':
matchingDevice.getDefaultIcon(condition.entityType),
};
// final cardData = {
// 'entityId': condition.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId': condition.entityId,
// 'title': matchingDevice.name ?? condition.entityId,
// 'productType': condition.entityType,
// 'imagePath':
// matchingDevice.getDefaultIcon(condition.entityType),
// };
final functions = matchingDevice.functions;
// final functions = matchingDevice.functions;
for (var function in functions) {
if (function.code == condition.expr.statusCode) {
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: condition.entityId,
functionCode: condition.expr.statusCode,
value: condition.expr.statusValue,
operationName: function.operationName,
),
];
break;
}
}
// for (var function in functions) {
// if (function.code == condition.expr.statusCode) {
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: condition.entityId,
// functionCode: condition.expr.statusCode,
// value: condition.expr.statusValue,
// operationName: function.operationName,
// ),
// ];
// break;
// }
// }
return cardData;
}).toList() ??
[];
// return cardData;
// }).toList() ??
// [];
final thenItems = actions.map((action) {
final matchingDevice = state.devices.firstWhere(
(device) => device.uuid == action.entityId,
orElse: () => AllDevicesModel(
uuid: action.entityId,
name: action.entityId,
productType: action.productType,
),
);
// final thenItems = actions.map((action) {
// final matchingDevice = state.devices.firstWhere(
// (device) => device.uuid == action.entityId,
// orElse: () => AllDevicesModel(
// uuid: action.entityId,
// name: action.entityId,
// productType: action.productType,
// ),
// );
final cardData = {
'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(),
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: (matchingDevice.name ?? 'Device'),
'productType': action.productType,
'imagePath': matchingDevice.getDefaultIcon(action.productType),
};
// final cardData = {
// 'entityId': action.entityId,
// 'uniqueCustomId': const Uuid().v4(),
// 'deviceId':
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
// 'title': action.actionExecutor == 'delay'
// ? 'Delay'
// : (matchingDevice.name ?? 'Device'),
// 'productType': action.productType,
// 'imagePath': matchingDevice.getDefaultIcon(action.productType),
// };
final functions = matchingDevice.functions;
// final functions = matchingDevice.functions;
if (action.executorProperty != null && action.actionExecutor != 'delay') {
final functionCode = action.executorProperty!.functionCode;
for (var function in functions) {
if (function.code == functionCode) {
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: action.entityId,
functionCode: functionCode ?? '',
value: action.executorProperty!.functionValue,
operationName: function.operationName,
),
];
break;
}
}
} else if (action.actionExecutor == 'delay') {
final delayFunction = DelayFunction(
deviceId: action.entityId,
deviceName: 'Delay',
);
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
DeviceFunctionData(
entityId: action.entityId,
functionCode: 'delay',
value: action.executorProperty?.delaySeconds ?? 0,
operationName: delayFunction.operationName,
),
];
}
// if (action.executorProperty != null && action.actionExecutor != 'delay') {
// final functionCode = action.executorProperty!.functionCode;
// for (var function in functions) {
// if (function.code == functionCode) {
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: functionCode ?? '',
// value: action.executorProperty!.functionValue,
// operationName: function.operationName,
// ),
// ];
// break;
// }
// }
// } else if (action.actionExecutor == 'delay') {
// final delayFunction = DelayFunction(
// deviceId: action.entityId,
// deviceName: 'Delay',
// );
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
// DeviceFunctionData(
// entityId: action.entityId,
// functionCode: 'delay',
// value: action.executorProperty?.delaySeconds ?? 0,
// operationName: delayFunction.operationName,
// ),
// ];
// }
return cardData;
}).toList();
// return cardData;
// }).toList();
return (thenItems, ifItems, currentFunctions);
}
// return (thenItems, ifItems, currentFunctions);
// }
Future<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> emit) async {
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
try {
emit(state.copyWith(
isLoading: true,
@ -719,42 +684,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
? '${action.entityId}_automation'
: action.actionExecutor == 'delay'
? '${action.entityId}_delay'
: action.entityId;
: const Uuid().v4();
if (!deviceCards.containsKey(deviceId)) {
deviceCards[deviceId] = {
'entityId': action.entityId,
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId':
action.type == 'automation' || action.actionExecutor == 'delay'
? const Uuid().v4()
: action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: action.type == 'automation'
? action.name ?? 'Automation'
: (matchingDevice?.name ?? 'Device'),
'productType': action.productType,
'functions': matchingDevice?.functions,
'imagePath': action.type == 'automation'
? Assets.automation
: action.actionExecutor == 'delay'
? Assets.delay
: matchingDevice?.getDefaultIcon(action.productType),
'device': matchingDevice,
'name': action.name,
'type': action.type,
};
}
// if (!deviceCards.containsKey(deviceId)) {
deviceCards[deviceId] = {
'entityId': action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
? action.entityId
: const Uuid().v4(),
'title': action.actionExecutor == 'delay'
? 'Delay'
: action.type == 'automation'
? action.name ?? 'Automation'
: (matchingDevice?.name ?? 'Device'),
'productType': action.productType,
'functions': matchingDevice?.functions,
'imagePath': action.type == 'automation'
? Assets.automation
: action.actionExecutor == 'delay'
? Assets.delay
: matchingDevice?.getDefaultIcon(action.productType),
'device': matchingDevice,
'name': action.name,
'type': action.type,
'tag': matchingDevice?.deviceTags?.isNotEmpty ?? false
? matchingDevice?.deviceTags![0].name ?? ''
: '',
'subSpace': matchingDevice?.deviceSubSpace?.subspaceName ?? '',
};
// }
final cardData = deviceCards[deviceId]!;
final uniqueCustomId = cardData['uniqueCustomId'].toString();
if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = [];
}
if (action.type == 'automation') {
if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = [];
}
updatedFunctions[uniqueCustomId]!.add(
DeviceFunctionData(
entityId: action.entityId,
@ -764,14 +732,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
),
);
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
} else if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = [];
}
final functions = matchingDevice?.functions;
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
final functions = matchingDevice?.functions ?? [];
final functionCode = action.executorProperty?.functionCode;
for (DeviceFunction function in functions ?? []) {
for (DeviceFunction function in functions) {
if (function.code == functionCode) {
updatedFunctions[uniqueCustomId]!.add(
DeviceFunctionData(
@ -785,9 +749,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
} else if (action.actionExecutor == 'delay') {
if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = [];
}
final delayFunction = DelayFunction(
deviceId: action.entityId,
deviceName: 'Delay',
@ -837,8 +798,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onResetRoutineState(
ResetRoutineState event, Emitter<RoutineState> emit) {
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
emit(state.copyWith(
ifItems: [],
thenItems: [],
@ -861,6 +821,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isUpdate: false,
createRoutineView: false));
}
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
try {
final projectId = await ProjectManager.getProjectUUID() ?? '';
@ -900,7 +861,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
// FutureOr<void> _deleteAutomation(DeleteAutomation event, Emitter<RoutineState> emit) {
// try {
// emit(state.copyWith(isLoading: true));
@ -915,8 +876,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
// }
// }
FutureOr<void> _fetchDevices(
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true));
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
@ -925,21 +885,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
var createRoutineBloc = context.read<CreateRoutineBloc>();
var spaceBloc = context.read<SpaceTreeBloc>();
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
devices.addAll(
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
}
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
}
emit(state.copyWith(isLoading: false, devices: devices));
@ -948,8 +904,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onUpdateScene(
UpdateScene event, Emitter<RoutineState> emit) async {
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
try {
// Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) {
@ -963,8 +918,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith(
errorMessage:
'A delay condition cannot be the only or the last action',
errorMessage: 'A delay condition cannot be the only or the last action',
isLoading: false,
));
return;
@ -1017,8 +971,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions,
);
final result =
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) {
add(ResetRoutineState());
add(const LoadScenes());
@ -1037,8 +990,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
FutureOr<void> _onUpdateAutomation(
UpdateAutomation event, Emitter<RoutineState> emit) async {
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
try {
if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith(
@ -1203,21 +1155,25 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
),
);
final deviceId = condition.entityId;
final deviceId = const Uuid().v4();
if (!deviceIfCards.containsKey(deviceId)) {
deviceIfCards[deviceId] = {
'entityId': condition.entityId,
'deviceId': condition.entityId,
'uniqueCustomId': const Uuid().v4(),
'title': matchingDevice.name ?? 'Device',
'productType': condition.productType,
'functions': matchingDevice.functions,
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
'device': matchingDevice,
'type': 'condition',
};
}
// if (!deviceIfCards.containsKey(deviceId)) {
deviceIfCards[deviceId] = {
'entityId': condition.entityId,
'deviceId': condition.entityId,
'uniqueCustomId': const Uuid().v4(),
'title': matchingDevice.name ?? 'Device',
'productType': condition.productType,
'functions': matchingDevice.functions,
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
'device': matchingDevice,
'type': 'condition',
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
? matchingDevice.deviceTags![0].name
: '',
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
};
// }
final cardData = deviceIfCards[deviceId]!;
final uniqueCustomId = cardData['uniqueCustomId'].toString();
@ -1253,37 +1209,38 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
),
);
final deviceId = action.actionExecutor == 'delay'
? '${action.entityId}_delay'
: action.entityId;
final deviceId = const Uuid().v4();
if (!deviceThenCards.containsKey(deviceId)) {
deviceThenCards[deviceId] = {
'entityId': action.entityId,
'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': const Uuid().v4(),
'title': action.actionExecutor == 'delay'
? 'Delay'
: (action.type == 'scene' || action.type == 'automation')
? action.name
: (matchingDevice.name ?? 'Device'),
'productType': action.productType,
'functions': matchingDevice.functions,
'imagePath': action.actionExecutor == 'delay'
? Assets.delay
: action.type == 'automation'
? Assets.automation
: matchingDevice.getDefaultIcon(action.productType),
'device': matchingDevice,
'type': action.type == 'scene'
? 'scene'
: action.type == 'automation'
? 'automation'
: 'action',
'icon': action.icon ?? ''
};
}
// if (!deviceThenCards.containsKey(deviceId)) {
deviceThenCards[deviceId] = {
'entityId': action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': const Uuid().v4(),
'title': action.actionExecutor == 'delay'
? 'Delay'
: (action.type == 'scene' || action.type == 'automation')
? action.name
: (matchingDevice.name ?? 'Device'),
'productType': action.productType,
'functions': matchingDevice.functions,
'imagePath': action.actionExecutor == 'delay'
? Assets.delay
: action.type == 'automation'
? Assets.automation
: matchingDevice.getDefaultIcon(action.productType),
'device': matchingDevice,
'type': action.type == 'scene'
? 'scene'
: action.type == 'automation'
? 'automation'
: 'action',
'icon': action.icon ?? '',
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
? matchingDevice.deviceTags![0].name
: '',
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
};
// }
final cardData = deviceThenCards[deviceId]!;
final uniqueCustomId = cardData['uniqueCustomId'].toString();
@ -1292,8 +1249,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
updatedFunctions[uniqueCustomId] = [];
}
if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
if (action.executorProperty != null && action.actionExecutor != 'delay') {
final functions = matchingDevice.functions;
final functionCode = action.executorProperty!.functionCode;
for (var function in functions) {
@ -1335,14 +1291,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
final ifItems = deviceIfCards.values
.where((card) => card['type'] == 'condition')
.toList();
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
final thenItems = deviceThenCards.values
.where((card) =>
card['type'] == 'action' ||
card['type'] == 'automation' ||
card['type'] == 'scene')
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
.toList();
emit(state.copyWith(
@ -1364,8 +1316,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<RoutineState> emit) async {
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
emit(state.copyWith(loadingSceneId: event.sceneId));
try {
@ -1407,29 +1358,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (success) {
final updatedAutomations = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
projectId);
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
// Remove from loading set safely
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
automations: updatedAutomations,
loadingAutomationIds: updatedLoadingIds,
));
} else {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update failed',
));
}
} catch (e) {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update error: ${e.toString()}',

View File

@ -63,7 +63,11 @@ class _CreateNewRoutinesDialogState extends State<CreateNewRoutinesDialog> {
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: CommunityDropdown(
communities: _bloc.communities,
communities: _bloc.communities..sort(
(a, b) => a.name.toLowerCase().compareTo(
b.name.toLowerCase(),
),
),
selectedValue: _selectedCommunity,
onChanged: (String? newValue) {
setState(() {

View File

@ -3,7 +3,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
@ -50,46 +51,45 @@ class DeviceDialogHelper {
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
if (removeComparetors && data['productType'] != 'WPS') {
//remove the current temp function in the 'if container'
functions.removeAt(3);
}
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors,
context: context,
functions: functions,
device: data['device'],
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors,
dialogType: dialogType,
);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
dialogType: dialogType,
context: context,
functions: functions,
device: data['device'],
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
dialogType: dialogType,
context: context,
functions: functions,
device: data['device'],
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
dialogType: dialogType,
context: context,
functions: functions,
device: data['device'],
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case 'WPS':
return WallPresenceSensor.showWPSFunctionsDialog(
dialogType: dialogType,
@ -99,12 +99,22 @@ class DeviceDialogHelper {
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
removeComparetors: removeComparetors);
case 'CPS':
return CeilingSensorHelper.showCeilingSensorDialog(
context: context,
functions: functions,
device: data['device'],
deviceSelectedFunctions: deviceSelectedFunctions,
uniqueCustomId: data['uniqueCustomId'],
dialogType: dialogType,
);
case 'GW':
return GatewayHelper.showGatewayFunctionsDialog(
context: context,
functions: functions,
uniqueCustomId: data['uniqueCustomId'],
deviceSelectedFunctions: deviceSelectedFunctions,
device: data['device'],
);
default:

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
@ -14,13 +14,18 @@ class SaveRoutineHelper {
static Future<void> showSaveRoutineDialog(BuildContext context) async {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
builder: (context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
final selectedConditionLabel = state.selectedAutomationOperator == 'and'
? 'All Conditions are met'
: 'Any Condition is met';
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: Container(
width: 600,
width: context.screenWidth * 0.5,
height: 500,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
@ -28,146 +33,42 @@ class SaveRoutineHelper {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DialogHeader('Create a scene: ${state.routineName ?? ""}'),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Left side - IF
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'IF:',
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
if (state.isTabToRun)
ListTile(
leading: SvgPicture.asset(
Assets.tabToRun,
width: 24,
height: 24,
),
title: const Text('Tab to run'),
),
if (state.isAutomation)
...state.ifItems.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
return ListTile(
leading: SvgPicture.asset(
item['imagePath'],
width: 22,
height: 22,
),
title:
Text(item['title'], style: const TextStyle(fontSize: 14)),
subtitle: Wrap(
children: functions
.map((f) => Text(
'${f.operationName}: ${f.value}, ',
style: const TextStyle(
color: ColorsManager.grayColor, fontSize: 8),
overflow: TextOverflow.ellipsis,
maxLines: 3,
))
.toList(),
),
);
}),
],
),
const SizedBox(height: 18),
Text(
'Create a scene: ${state.routineName ?? ""}',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,
),
const SizedBox(width: 16),
// Right side - THEN items
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'THEN:',
style: TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 8),
...state.thenItems.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
return ListTile(
leading: item['type'] == 'tap_to_run' || item['type'] == 'scene'
? Image.memory(
base64Decode(item['icon']),
width: 22,
height: 22,
)
: SvgPicture.asset(
item['imagePath'],
width: 22,
height: 22,
),
title: Text(
item['title'],
style: context.textTheme.bodySmall?.copyWith(
fontSize: 14,
color: ColorsManager.grayColor,
),
),
subtitle: Wrap(
children: functions
.map((f) => Text(
'${f.operationName}: ${f.value}, ',
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.grayColor, fontSize: 8),
overflow: TextOverflow.ellipsis,
maxLines: 3,
))
.toList(),
),
);
}),
],
),
const SizedBox(height: 18),
_buildDivider(),
_buildListsLabelRow(selectedConditionLabel),
Expanded(
child: Padding(
padding: const EdgeInsetsDirectional.symmetric(
horizontal: 16,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 24,
children: [
_buildIfConditions(state, context),
Container(
width: 1,
color: ColorsManager.greyColor.withValues(alpha: 0.8),
),
),
],
_buildThenActions(state, context),
],
),
),
),
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: Text(
// state.errorMessage!,
// style: const TextStyle(color: Colors.red),
// ),
// ),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: () async {
if (state.isAutomation) {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateAutomation());
} else {
context.read<RoutineBloc>().add(const CreateAutomationEvent());
}
} else {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateScene());
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
}
}
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
Navigator.pop(context);
// }
},
isConfirmEnabled: true,
),
_buildDivider(),
const SizedBox(height: 8),
_buildDialogFooter(context, state),
const SizedBox(height: 8),
],
),
),
@ -177,4 +78,245 @@ class SaveRoutineHelper {
},
);
}
static Container _buildDivider() {
return Container(
height: 1,
width: double.infinity,
color: ColorsManager.greyColor,
);
}
static Widget _buildListsLabelRow(String selectedConditionLabel) {
const textStyle = TextStyle(
fontSize: 16,
);
return Container(
color: ColorsManager.backgroundColor.withValues(alpha: 0.5),
padding: const EdgeInsetsDirectional.all(20),
child: Row(
spacing: 16,
children: [
Expanded(child: Text('IF: $selectedConditionLabel', style: textStyle)),
const Expanded(child: Text('THEN:', style: textStyle)),
],
),
);
}
static Widget _buildDialogFooter(BuildContext context, RoutineState state) {
return Row(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
DialogFooterButton(
text: 'Cancel',
onTap: () => Navigator.pop(context),
),
DialogFooterButton(
text: 'Confirm',
onTap: () {
if (state.isAutomation) {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateAutomation());
} else {
context.read<RoutineBloc>().add(const CreateAutomationEvent());
}
} else {
if (state.isUpdate ?? false) {
context.read<RoutineBloc>().add(const UpdateScene());
} else {
context.read<RoutineBloc>().add(const CreateSceneEvent());
}
}
Navigator.pop(context);
},
textColor: ColorsManager.primaryColorWithOpacity,
),
],
);
}
static Widget _buildThenActions(RoutineState state, BuildContext context) {
return Expanded(
child: ListView(
// shrinkWrap: true,
children: state.thenItems.map((item) {
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functionRow(item, context, functions);
}).toList(),
),
);
}
static Widget _buildIfConditions(RoutineState state, BuildContext context) {
return Expanded(
child: ListView(
// shrinkWrap: true,
children: [
if (state.isTabToRun)
ListTile(
leading: SvgPicture.asset(
Assets.tabToRun,
width: 24,
height: 24,
),
title: const Text('Tab to run'),
),
if (state.isAutomation)
...state.ifItems.map((item) {
final functions =
state.selectedFunctions[item['uniqueCustomId']] ?? [];
return functionRow(item, context, functions);
}),
],
),
);
}
static Widget functionRow(
dynamic item,
BuildContext context,
List<DeviceFunctionData> functions,
) {
return Padding(
padding: const EdgeInsets.only(top: 6),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 17,
children: [
Container(
width: 22,
height: 22,
padding: const EdgeInsetsDirectional.all(4),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: ColorsManager.textFieldGreyColor,
border: Border.all(
color: ColorsManager.neutralGray,
width: 1.5,
),
),
child: Center(
child: item['type'] == 'tap_to_run' || item['type'] == 'scene'
? Image.memory(
base64Decode(item['icon']),
width: 12,
height: 22,
fit: BoxFit.scaleDown,
)
: SvgPicture.asset(
item['imagePath'],
width: 12,
height: 12,
fit: BoxFit.scaleDown,
),
),
),
Flexible(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 2,
children: [
Text(
item['title'],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: context.textTheme.bodySmall?.copyWith(
fontSize: 15,
color: ColorsManager.textPrimaryColor,
),
),
Wrap(
runSpacing: 16,
spacing: 4,
children: functions
.map(
(function) => Text(
'${function.operationName}: ${function.value}',
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.grayColor,
fontSize: 8,
),
overflow: TextOverflow.ellipsis,
maxLines: 3,
),
)
.toList(),
),
],
),
),
],
),
),
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 2,
children: [
Visibility(
visible: item['tag'] != null && item['tag'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8,
height: 8,
child: SvgPicture.asset(
Assets.deviceTagIcon,
),
),
Text(
item['tag'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
],
),
),
Visibility(
visible: item['subSpace'] != null && item['subSpace'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8,
height: 8,
child: SvgPicture.asset(
Assets.spaceLocationIcon,
),
),
Text(
item['subSpace'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
],
),
);
}
}

View File

@ -5,23 +5,28 @@ import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
abstract class ACFunction extends DeviceFunction<AcStatusModel> {
final String type;
ACFunction({
required super.deviceId,
required super.deviceName,
required super.code,
required super.operationName,
required super.icon,
required this.type,
});
List<ACOperationalValue> getOperationalValues();
}
class SwitchFunction extends ACFunction {
SwitchFunction({required super.deviceId, required super.deviceName})
SwitchFunction(
{required super.deviceId, required super.deviceName, required type})
: super(
code: 'switch',
operationName: 'Power',
icon: Assets.assetsAcPower,
type: type,
);
@override
@ -40,11 +45,13 @@ class SwitchFunction extends ACFunction {
}
class ModeFunction extends ACFunction {
ModeFunction({required super.deviceId, required super.deviceName})
ModeFunction(
{required super.deviceId, required super.deviceName, required type})
: super(
code: 'mode',
operationName: 'Mode',
icon: Assets.assetsFreezing,
type: type,
);
@override
@ -72,7 +79,8 @@ class TempSetFunction extends ACFunction {
final int max;
final int step;
TempSetFunction({required super.deviceId, required super.deviceName})
TempSetFunction(
{required super.deviceId, required super.deviceName, required type})
: min = 160,
max = 300,
step = 1,
@ -80,6 +88,7 @@ class TempSetFunction extends ACFunction {
code: 'temp_set',
operationName: 'Set Temperature',
icon: Assets.assetsTempreture,
type: type,
);
@override
@ -97,8 +106,10 @@ class TempSetFunction extends ACFunction {
}
class LevelFunction extends ACFunction {
LevelFunction({required super.deviceId, required super.deviceName})
LevelFunction(
{required super.deviceId, required super.deviceName, required type})
: super(
type: type,
code: 'level',
operationName: 'Fan Speed',
icon: Assets.assetsFanSpeed,
@ -130,8 +141,10 @@ class LevelFunction extends ACFunction {
}
class ChildLockFunction extends ACFunction {
ChildLockFunction({required super.deviceId, required super.deviceName})
ChildLockFunction(
{required super.deviceId, required super.deviceName, required type})
: super(
type: type,
code: 'child_lock',
operationName: 'Child Lock',
icon: Assets.assetsChildLock,
@ -157,11 +170,13 @@ class CurrentTempFunction extends ACFunction {
final int max;
final int step;
CurrentTempFunction({required super.deviceId, required super.deviceName})
CurrentTempFunction(
{required super.deviceId, required super.deviceName, required type})
: min = -100,
max = 990,
step = 1,
super(
type: type,
code: 'temp_current',
operationName: 'Current Temperature',
icon: Assets.currentTemp,

View File

@ -0,0 +1,889 @@
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class CpsOperationalValue {
final String icon;
final String description;
final dynamic value;
CpsOperationalValue({
required this.icon,
required this.description,
required this.value,
});
}
abstract class CpsFunctions extends DeviceFunction<CpsOperationalValue> {
CpsFunctions({
required super.deviceId,
required super.deviceName,
required super.code,
required super.operationName,
required super.icon,
required this.type,
});
final String type;
List<CpsOperationalValue> getOperationalValues();
}
final class CpsRadarSwitchFunction extends CpsFunctions {
CpsRadarSwitchFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'radar_switch',
operationName: 'Radar Switch',
icon: Assets.acPower,
);
@override
List<CpsOperationalValue> getOperationalValues() => [
CpsOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
CpsOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
final class CpsSpatialParameterSwitchFunction extends CpsFunctions {
CpsSpatialParameterSwitchFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'space_para_switch',
operationName: 'Spatial Parameter Switch',
icon: Assets.acPower,
);
@override
List<CpsOperationalValue> getOperationalValues() => [
CpsOperationalValue(
icon: Assets.assetsAcPower,
description: "ON",
value: true,
),
CpsOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "OFF",
value: false,
),
];
}
final class CpsSensitivityFunction extends CpsFunctions {
CpsSensitivityFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 1,
max = 10,
step = 1,
super(
code: 'sensitivity',
operationName: 'Sensitivity',
icon: Assets.sensitivity,
);
final int min;
final int max;
final int step;
static const _images = <String>[
Assets.sensitivityFeature1,
Assets.sensitivityFeature1,
Assets.sensitivityFeature2,
Assets.sensitivityFeature3,
Assets.sensitivityFeature4,
Assets.sensitivityFeature5,
Assets.sensitivityFeature6,
Assets.sensitivityFeature7,
Assets.sensitivityFeature8,
Assets.sensitivityFeature9,
Assets.sensitivityFeature9,
];
@override
List<CpsOperationalValue> getOperationalValues() {
final values = <CpsOperationalValue>[];
for (var value = min; value <= max; value += step) {
values.add(
CpsOperationalValue(
icon: _images[value],
description: '$value',
value: value,
),
);
}
return values;
}
}
final class CpsMovingSpeedFunction extends CpsFunctions {
CpsMovingSpeedFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0,
max = 32,
step = 1,
super(
code: 'moving_speed',
operationName: 'Moving Speed',
icon: Assets.speedoMeter,
);
final int min;
final int max;
final int step;
@override
List<CpsOperationalValue> getOperationalValues() {
return List.generate(
(max - min) ~/ step + 1,
(index) => CpsOperationalValue(
icon: Assets.speedoMeter,
description: '${min + (index * step)}',
value: min + (index * step),
),
);
}
}
final class CpsSpatialStaticValueFunction extends CpsFunctions {
CpsSpatialStaticValueFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0,
max = 255,
step = 1,
super(
code: 'space_static_val',
operationName: 'Spacial Static Value',
icon: Assets.spatialStaticValue,
);
final int min;
final int max;
final int step;
@override
List<CpsOperationalValue> getOperationalValues() {
return List.generate(
(max - min) ~/ step + 1,
(index) => CpsOperationalValue(
icon: Assets.spatialStaticValue,
description: '${min + (index * step)}',
value: min + (index * step),
),
);
}
}
final class CpsSpatialMotionValueFunction extends CpsFunctions {
CpsSpatialMotionValueFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0,
max = 255,
step = 1,
super(
code: 'space_move_val',
operationName: 'Spatial Motion Value',
icon: Assets.spatialMotionValue,
);
final int min;
final int max;
final int step;
@override
List<CpsOperationalValue> getOperationalValues() {
return List.generate(
(max - min) ~/ step + 1,
(index) => CpsOperationalValue(
icon: Assets.spatialMotionValue,
description: '${min + (index * step)}',
value: min + (index * step),
),
);
}
}
final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions {
CpsMaxDistanceOfDetectionFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 10.0,
step = 0.5,
super(
code: 'moving_max_dis',
operationName: 'Maximum Distance Of Detection',
icon: Assets.currentDistanceIcon,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.currentDistanceIcon,
description: '${value.toStringAsFixed(1)} M',
value: value,
);
},
);
}
}
final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions {
CpsMaxDistanceOfStaticDetectionFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 10.0,
step = 0.5,
super(
code: 'static_max_dis',
operationName: 'Maximum Distance Of Static Detection',
icon: Assets.currentDistanceIcon,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.currentDistanceIcon,
description: '${value.toStringAsFixed(1)} M',
value: value,
);
},
);
}
}
final class CpsDetectionRangeFunction extends CpsFunctions {
CpsDetectionRangeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 25.5,
step = 0.1,
super(
code: 'moving_range',
operationName: 'Detection Range',
icon: Assets.farDetection,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.farDetection,
description: '${value.toStringAsFixed(1)} M',
value: value,
);
},
);
}
}
final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions {
CpsDistanceOfMovingObjectsFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 25.5,
step = 0.1,
super(
code: 'presence_range',
operationName: 'Distance Of Moving Objects',
icon: Assets.currentDistanceIcon,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.currentDistanceIcon,
description: '${value.toStringAsFixed(1)} M',
value: value,
);
},
);
}
}
final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
CpsPresenceJudgementThrsholdFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0,
max = 255,
step = 5,
super(
code: 'presence_reference',
operationName: 'Presence Judgement Threshold',
icon: Assets.presenceJudgementThrshold,
);
final int min;
final int max;
final int step;
@override
List<CpsOperationalValue> getOperationalValues() {
return List.generate(
(max - min) ~/ step + 1,
(index) => CpsOperationalValue(
icon: Assets.presenceJudgementThrshold,
description: '${min + (index * step)}',
value: min + (index * step),
),
);
}
}
final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
CpsMotionAmplitudeTriggerThresholdFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0,
max = 255,
step = 5,
super(
code: 'moving_reference',
operationName: 'Motion Amplitude Trigger Threshold',
icon: Assets.presenceJudgementThrshold,
);
final int min;
final int max;
final int step;
@override
List<CpsOperationalValue> getOperationalValues() {
return List.generate(
(max - min) ~/ step + 1,
(index) => CpsOperationalValue(
icon: Assets.presenceJudgementThrshold,
description: '${min + (index * step)}',
value: min + (index * step),
),
);
}
}
final class CpsPerpetualBoundaryFunction extends CpsFunctions {
CpsPerpetualBoundaryFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.00,
max = 5.00,
step = 0.50,
super(
code: 'perceptual_boundary',
operationName: 'Perpetual Boundary',
icon: Assets.boundary,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.boundary,
description: '${value.toStringAsFixed(1)}M',
value: value + 1200,
);
},
);
}
}
final class CpsMotionTriggerBoundaryFunction extends CpsFunctions {
CpsMotionTriggerBoundaryFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 5.0,
step = 0.5,
super(
code: 'moving_boundary',
operationName: 'Motion Trigger Boundary',
icon: Assets.motionMeter,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.motionMeter,
description: '${value.toStringAsFixed(1)} M',
value: value,
);
},
);
}
}
final class CpsMotionTriggerTimeFunction extends CpsFunctions {
CpsMotionTriggerTimeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 2.0,
step = 0.1,
super(
code: 'moving_rigger_time',
operationName: 'Motion Trigger Time',
icon: Assets.motionMeter,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.motionMeter,
description: '${value.toStringAsFixed(3)} sec',
value: value,
);
},
);
}
}
final class CpsMotionToStaticTimeFunction extends CpsFunctions {
CpsMotionToStaticTimeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 50.0,
step = 1.0,
super(
code: 'moving_static_time',
operationName: 'Motion To Static Time',
icon: Assets.motionMeter,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.motionMeter,
description: '${value.toStringAsFixed(0)} sec',
value: value,
);
},
);
}
}
final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions {
CpsEnteringNoBodyStateTimeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 0.0,
max = 300.0,
step = 5.0,
super(
code: 'none_body_time',
operationName: 'Entering Nobody State Time',
icon: Assets.motionMeter,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.motionMeter,
description: '${value.toStringAsFixed(0)} sec',
value: value,
);
},
);
}
}
final class CpsSelfTestResultFunctions extends CpsFunctions {
CpsSelfTestResultFunctions({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'checking_result',
operationName: 'Self-Test Result',
icon: Assets.selfTestResult,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
description: 'Self Testing',
icon: Assets.selfTestResult,
value: 'check',
),
CpsOperationalValue(
description: 'Self Testing Success',
icon: Assets.selfTestingSuccess,
value: 'check_success',
),
CpsOperationalValue(
description: 'Self Testing Failure',
icon: Assets.selfTestingFailure,
value: 'check_failure',
),
CpsOperationalValue(
description: 'Self Testing Timeout',
icon: Assets.selfTestingTimeout,
value: 'check_timeout',
),
CpsOperationalValue(
description: 'Communication Fault',
icon: Assets.communicationFault,
value: 'communication_fault',
),
CpsOperationalValue(
description: 'Radar Fault',
icon: Assets.radarFault,
value: 'radar_fault',
),
];
}
}
final class CpsNobodyTimeFunction extends CpsFunctions {
CpsNobodyTimeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'nobody_time',
operationName: 'Entering Nobody Time',
icon: Assets.assetsNobodyTime,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: 'None',
value: 'none',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '10sec',
value: '10s',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '30sec',
value: '30s',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '1min',
value: '1min',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '2min',
value: '2min',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '5min',
value: '5min',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '10min',
value: '10min',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '30min',
value: '30min',
),
CpsOperationalValue(
icon: Assets.assetsNobodyTime,
description: '1hour',
value: '1hr',
),
];
}
}
final class CpsMovementFunctions extends CpsFunctions {
CpsMovementFunctions({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'body_movement',
operationName: 'Movement',
icon: Assets.motion,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
description: 'None',
icon: Assets.nobodyTime,
value: 'none',
),
CpsOperationalValue(
description: 'Close To',
icon: Assets.closeToMotion,
value: 'close_to',
),
CpsOperationalValue(
description: 'Far Away',
icon: Assets.farAwayMotion,
value: 'far_away',
),
];
}
}
final class CpsCustomModeFunction extends CpsFunctions {
CpsCustomModeFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'custom_mode',
operationName: 'Custom Mode',
icon: Assets.cpsCustomMode,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
icon: Assets.cpsMode1,
description: 'Mode 1',
value: 'mode1',
),
CpsOperationalValue(
icon: Assets.cpsMode2,
description: 'Mode 2',
value: 'mode2',
),
CpsOperationalValue(
icon: Assets.cpsMode3,
description: 'Mode 3',
value: 'mode3',
),
CpsOperationalValue(
icon: Assets.cpsMode4,
description: 'Mode 4',
value: 'mode4',
),
];
}
}
final class CpsSpaceTypeFunctions extends CpsFunctions {
CpsSpaceTypeFunctions({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'scene',
operationName: 'Space Type',
icon: Assets.spaceType,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
description: 'Office',
icon: Assets.office,
value: 'office',
),
CpsOperationalValue(
description: 'Parlour',
icon: Assets.parlour,
value: 'parlour',
),
CpsOperationalValue(
description: 'Bathroom',
icon: Assets.bathroom,
value: 'bathroom',
),
CpsOperationalValue(
description: 'Bedroom',
icon: Assets.bedroom,
value: 'bedroom',
),
CpsOperationalValue(
description: 'DIY',
icon: Assets.dyi,
value: 'dyi',
),
];
}
}
class CpsPresenceStatusFunctions extends CpsFunctions {
CpsPresenceStatusFunctions({
required super.deviceId,
required super.deviceName,
required super.type,
}) : super(
code: 'presence_state',
operationName: 'Presence Status',
icon: Assets.presenceSensor,
);
@override
List<CpsOperationalValue> getOperationalValues() {
return [
CpsOperationalValue(
icon: Assets.nobodyTime,
description: 'None',
value: 'none',
),
CpsOperationalValue(
icon: Assets.presenceState,
description: 'Presence',
value: 'presence',
),
CpsOperationalValue(
icon: Assets.motion,
description: 'Motion',
value: 'motion',
),
];
}
}
final class CpsSportsParaFunction extends CpsFunctions {
CpsSportsParaFunction({
required super.deviceId,
required super.deviceName,
required super.type,
}) : min = 1,
max = 100,
step = 1,
super(
code: 'sports_para',
operationName: 'Sports Para',
icon: Assets.sportsPara,
);
final double min;
final double max;
final double step;
@override
List<CpsOperationalValue> getOperationalValues() {
final count = ((max - min) / step).round() + 1;
return List.generate(
count,
(index) {
final value = (min + (index * step));
return CpsOperationalValue(
icon: Assets.motionMeter,
description: value.toStringAsFixed(0),
value: value,
);
},
);
}
}

View File

@ -3,7 +3,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/switch_operation
import 'package:syncrow_web/utils/constants/assets.dart';
class ThreeGangSwitch1Function extends BaseSwitchFunction {
ThreeGangSwitch1Function({required super.deviceId, required super.deviceName})
ThreeGangSwitch1Function({required super.deviceId, required super.deviceName ,required type})
: super(
code: 'switch_1',
operationName: 'Light 1 Switch',
@ -26,7 +26,7 @@ class ThreeGangSwitch1Function extends BaseSwitchFunction {
}
class ThreeGangCountdown1Function extends BaseSwitchFunction {
ThreeGangCountdown1Function({required super.deviceId, required super.deviceName})
ThreeGangCountdown1Function({required super.deviceId, required super.deviceName ,required type})
: super(
code: 'countdown_1',
operationName: 'Light 1 Countdown',
@ -47,7 +47,7 @@ class ThreeGangCountdown1Function extends BaseSwitchFunction {
}
class ThreeGangSwitch2Function extends BaseSwitchFunction {
ThreeGangSwitch2Function({required super.deviceId, required super.deviceName})
ThreeGangSwitch2Function({required super.deviceId, required super.deviceName, required type})
: super(
code: 'switch_2',
operationName: 'Light 2 Switch',
@ -70,7 +70,7 @@ class ThreeGangSwitch2Function extends BaseSwitchFunction {
}
class ThreeGangCountdown2Function extends BaseSwitchFunction {
ThreeGangCountdown2Function({required super.deviceId, required super.deviceName})
ThreeGangCountdown2Function({required super.deviceId, required super.deviceName ,required type})
: super(
code: 'countdown_2',
operationName: 'Light 2 Countdown',
@ -91,7 +91,7 @@ class ThreeGangCountdown2Function extends BaseSwitchFunction {
}
class ThreeGangSwitch3Function extends BaseSwitchFunction {
ThreeGangSwitch3Function({required super.deviceId, required super.deviceName})
ThreeGangSwitch3Function({required super.deviceId, required super.deviceName ,required type})
: super(
code: 'switch_3',
operationName: 'Light 3 Switch',
@ -114,7 +114,7 @@ class ThreeGangSwitch3Function extends BaseSwitchFunction {
}
class ThreeGangCountdown3Function extends BaseSwitchFunction {
ThreeGangCountdown3Function({required super.deviceId, required super.deviceName})
ThreeGangCountdown3Function({required super.deviceId, required super.deviceName ,required type})
: super(
code: 'countdown_3',
operationName: 'Light 3 Countdown',

View File

@ -33,10 +33,11 @@ final class GatewaySwitchAlarmSound extends GatewayFunctions {
required super.deviceId,
required super.deviceName,
required super.type,
super.code = 'switch_alarm_sound',
super.operationName = 'Switch Alarm Sound',
super.icon = Assets.activeBell,
});
}) : super(
code: 'switch_alarm_sound',
operationName: 'Switch Alarm Sound',
icon: Assets.activeBell,
);
@override
List<GatewayOperationalValue> getOperationalValues() => [
@ -58,10 +59,11 @@ final class GatewayMasterState extends GatewayFunctions {
required super.deviceId,
required super.deviceName,
required super.type,
super.code = 'master_state',
super.operationName = 'Master State',
super.icon = Assets.gear,
});
}) : super(
code: 'master_state',
operationName: 'Master State',
icon: Assets.gear,
);
@override
List<GatewayOperationalValue> getOperationalValues() {
@ -69,12 +71,12 @@ final class GatewayMasterState extends GatewayFunctions {
GatewayOperationalValue(
icon: Assets.assetsAcPower,
description: "Normal",
value: 'Normal',
value: 'normal',
),
GatewayOperationalValue(
icon: Assets.assetsAcPowerOFF,
description: "Alarm",
value: 'Alarm',
value: 'alarm',
),
];
}
@ -85,10 +87,11 @@ final class GatewayFactoryReset extends GatewayFunctions {
required super.deviceId,
required super.deviceName,
required super.type,
super.code = 'factory_reset',
super.operationName = 'Factory Reset',
super.icon = Assets.factoryReset,
});
}) : super(
code: 'factory_reset',
operationName: 'Factory Reset',
icon: Assets.factoryReset,
);
@override
List<GatewayOperationalValue> getOperationalValues() {

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/create_new_routines/create_new_routines.dart';
import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/widgets/main_routine_view/fetch_routi
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class RoutinesView extends StatefulWidget {
const RoutinesView({super.key});
@ -27,9 +28,9 @@ class _RoutinesViewState extends State<RoutinesView> {
if (result == null) return;
final communityId = result['community'];
final spaceId = result['space'];
final _bloc = BlocProvider.of<CreateRoutineBloc>(context);
final bloc = BlocProvider.of<CreateRoutineBloc>(context);
final routineBloc = context.read<RoutineBloc>();
_bloc.add(SaveCommunityIdAndSpaceIdEvent(
bloc.add(SaveCommunityIdAndSpaceIdEvent(
communityID: communityId, spaceID: spaceId));
await Future.delayed(const Duration(seconds: 1));
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
@ -49,18 +50,23 @@ class _RoutinesViewState extends State<RoutinesView> {
child: SpaceTreeView(
onSelect: () => context.read<RoutineBloc>()
..add(const LoadScenes())
..add(const LoadAutomation()),
..add(const LoadAutomation())
..add(FetchDevicesInRoutine()),
),
),
Expanded(
flex: 4,
child: ListView(
children: [
Container(
padding: const EdgeInsets.all(16),
height: MediaQuery.sizeOf(context).height,
child: SizedBox(
height: context.screenHeight,
width: context.screenWidth,
child: SingleChildScrollView(
padding: const EdgeInsetsDirectional.all(16),
child: Padding(
padding: const EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
spacing: 16,
children: [
Text(
"Create New Routines",
@ -70,7 +76,6 @@ class _RoutinesViewState extends State<RoutinesView> {
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
RoutineViewCard(
isLoading: false,
onChanged: (v) {},
@ -85,13 +90,11 @@ class _RoutinesViewState extends State<RoutinesView> {
icon: Icons.add,
textString: '',
),
const SizedBox(height: 15),
const Expanded(child: FetchRoutineScenesAutomation()),
const FetchRoutineScenesAutomation(),
],
),
),
const SizedBox(height: 50),
],
),
),
)
],

View File

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class ConditionToggle extends StatelessWidget {
final String? currentCondition;
final void Function(String condition) onChanged;
const ConditionToggle({
required this.onChanged,
this.currentCondition,
super.key,
});
static const _conditions = ["<", "==", ">"];
@override
Widget build(BuildContext context) {
return ToggleButtons(
onPressed: (index) => onChanged(_conditions[index]),
borderRadius: const BorderRadius.all(Radius.circular(8)),
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
selectedColor: Colors.white,
fillColor: ColorsManager.primaryColorWithOpacity,
color: ColorsManager.primaryColorWithOpacity,
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 40.0,
),
isSelected: _conditions.map((c) => c == (currentCondition ?? "==")).toList(),
children: _conditions.map((c) => Text(c)).toList(),
);
}
}

View File

@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget {
final int? dialogWidth;
const DialogFooter({
Key? key,
super.key,
required this.onCancel,
required this.onConfirm,
required this.isConfirmEnabled,
this.dialogWidth,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
@ -28,46 +28,52 @@ class DialogFooter extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
child: _buildFooterButton(
context,
'Cancel',
onCancel,
),
DialogFooterButton(
text: 'Cancel',
onTap: onCancel,
),
if (isConfirmEnabled) ...[
Container(width: 1, height: 50, color: ColorsManager.greyColor),
Expanded(
child: _buildFooterButton(
context,
'Confirm',
onConfirm,
),
DialogFooterButton(
text: 'Confirm',
onTap: onConfirm,
textColor: isConfirmEnabled
? ColorsManager.primaryColorWithOpacity
: Colors.red,
),
],
],
),
);
}
}
Widget _buildFooterButton(
BuildContext context,
String text,
VoidCallback? onTap,
) {
return GestureDetector(
onTap: onTap,
child: SizedBox(
height: 50,
child: Center(
child: Text(
text,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: text == 'Confirm'
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
),
class DialogFooterButton extends StatelessWidget {
const DialogFooterButton({
required this.text,
required this.onTap,
this.textColor,
super.key,
});
final String text;
final VoidCallback? onTap;
final Color? textColor;
@override
Widget build(BuildContext context) {
return Expanded(
child: TextButton(
style: TextButton.styleFrom(
foregroundColor: ColorsManager.primaryColorWithOpacity,
disabledForegroundColor: ColorsManager.primaryColor,
),
onPressed: onTap,
child: Text(
text,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: textColor ?? ColorsManager.textGray,
),
),
),
);

View File

@ -16,6 +16,7 @@ class DialogHeader extends StatelessWidget {
),
Text(
title,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.bold,

View File

@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class DraggableCard extends StatelessWidget {
@ -68,9 +69,9 @@ class DraggableCard extends StatelessWidget {
Card(
color: ColorsManager.whiteColors,
child: Container(
padding: padding ?? const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
width: 110,
height: deviceFunctions.isEmpty ? 123 : null,
height: deviceFunctions.isEmpty ? 160 : 180,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
@ -78,6 +79,7 @@ class DraggableCard extends StatelessWidget {
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
height: 50,
@ -112,8 +114,69 @@ class DraggableCard extends StatelessWidget {
),
),
),
const SizedBox(
height: 4,
),
Visibility(
visible: deviceData['tag'] != null && deviceData['tag'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8, height: 8, child: SvgPicture.asset(Assets.deviceTagIcon)),
Flexible(
child: Text(
deviceData['tag'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
Visibility(
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
child: const SizedBox(
height: 4,
),
),
Visibility(
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
child: Row(
spacing: 2,
children: [
SizedBox(
width: 8,
height: 8,
child: SvgPicture.asset(Assets.spaceLocationIcon)),
Flexible(
child: Text(
deviceData['subSpace'] ?? '',
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 9,
fontWeight: FontWeight.w400,
),
),
),
],
),
),
],
),
if (deviceFunctions.isNotEmpty)
const SizedBox(
height: 4,
),
if (deviceFunctions.isNotEmpty)
...deviceFunctions.map((function) => Row(
mainAxisSize: MainAxisSize.min,
@ -123,7 +186,7 @@ class DraggableCard extends StatelessWidget {
'${function.operationName}: ${_formatFunctionValue(function)}',
style: context.textTheme.bodySmall?.copyWith(
fontSize: 9,
color: ColorsManager.textGray,
color: ColorsManager.lightGreyColor,
height: 1.2,
),
maxLines: 2,
@ -162,7 +225,7 @@ class DraggableCard extends StatelessWidget {
if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
return '${(function.value / 10).toStringAsFixed(0)}°C';
} else if (function.functionCode.contains('countdown')) {
final seconds = function.value.toInt();
final seconds = function.value?.toInt() ?? 0;
if (seconds >= 3600) {
final hours = (seconds / 3600).floor();
final remainingMinutes = ((seconds % 3600) / 60).floor();

View File

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class FunctionSlider extends StatelessWidget {
final dynamic initialValue;
final (double min, double max) range;
final void Function(double value) onChanged;
final double dividendOfRange;
const FunctionSlider({
required this.onChanged,
required this.initialValue,
required this.range,
required this.dividendOfRange,
super.key,
});
@override
Widget build(BuildContext context) {
final (min, max) = range;
final bool isValidRange = max > min;
final double value = initialValue is int
? (initialValue as int).toDouble()
: (initialValue as double);
final int? divisions = isValidRange ? ((max - min) / dividendOfRange).round() : null;
return Slider(
value: value.clamp(min, max),
min: min,
max: max,
divisions: divisions,
onChanged: isValidRange ? onChanged : null,
);
}
}

View File

@ -17,91 +17,87 @@ class IfContainer extends StatelessWidget {
builder: (context, state) {
return DragTarget<Map<String, dynamic>>(
builder: (context, candidateData, rejectedData) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('IF',
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation && state.ifItems.isNotEmpty)
AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator),
],
),
const SizedBox(height: 16),
if (state.isTabToRun)
const Row(
mainAxisAlignment: MainAxisAlignment.center,
return SingleChildScrollView(
child: Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DraggableCard(
imagePath: Assets.tabToRun,
title: 'Tab to run',
deviceData: {},
),
const Text('IF',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
if (state.isAutomation && state.ifItems.isNotEmpty)
AutomationOperatorSelector(
selectedOperator: state.selectedAutomationOperator),
],
),
if (!state.isTabToRun)
Wrap(
spacing: 8,
runSpacing: 8,
children: List.generate(
state.ifItems.length,
(index) => GestureDetector(
onTap: () async {
if (!state.isTabToRun) {
final result = await DeviceDialogHelper
.showDeviceDialog(
context: context,
data: state.ifItems[index],
removeComparetors: false,
dialogType: "IF");
const SizedBox(height: 16),
if (state.isTabToRun)
const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
DraggableCard(
imagePath: Assets.tabToRun,
title: 'Tab to run',
deviceData: {},
),
],
),
if (!state.isTabToRun)
Wrap(
spacing: 8,
runSpacing: 8,
children: List.generate(
state.ifItems.length,
(index) => GestureDetector(
onTap: () async {
if (!state.isTabToRun) {
final result = await DeviceDialogHelper.showDeviceDialog(
context: context,
data: state.ifItems[index],
removeComparetors: false,
dialogType: "IF");
if (result != null) {
context.read<RoutineBloc>().add(
AddToIfContainer(
state.ifItems[index], false));
} else if (![
'AC',
'1G',
'2G',
'3G',
'WPS'
'GW',
].contains(
state.ifItems[index]['productType'])) {
context.read<RoutineBloc>().add(
AddToIfContainer(
state.ifItems[index], false));
if (result != null) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(state.ifItems[index], false));
} else if (![
'AC',
'1G',
'2G',
'3G',
'WPS',
'GW',
'CPS',
].contains(state.ifItems[index]['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(state.ifItems[index], false));
}
}
}
},
child: DraggableCard(
imagePath:
state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 8),
isFromThen: false,
isFromIf: true,
onRemove: () {
context.read<RoutineBloc>().add(
RemoveDragCard(
index: index,
isFromThen: false,
key: state.ifItems[index]
['uniqueCustomId']));
},
),
)),
),
],
child: DraggableCard(
imagePath: state.ifItems[index]['imagePath'] ?? '',
title: state.ifItems[index]['title'] ?? '',
deviceData: state.ifItems[index],
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
isFromThen: false,
isFromIf: true,
onRemove: () {
context.read<RoutineBloc>().add(RemoveDragCard(
index: index,
isFromThen: false,
key: state.ifItems[index]['uniqueCustomId']));
},
),
)),
),
],
),
),
);
},
@ -116,9 +112,7 @@ class IfContainer extends StatelessWidget {
if (!state.isTabToRun) {
if (mutableData['deviceId'] == 'tab_to_run') {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, true));
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, true));
} else {
final result = await DeviceDialogHelper.showDeviceDialog(
dialogType: 'IF',
@ -127,14 +121,10 @@ class IfContainer extends StatelessWidget {
removeComparetors: false);
if (result != null) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
.contains(mutableData['productType'])) {
context
.read<RoutineBloc>()
.add(AddToIfContainer(mutableData, false));
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
}
}
}
@ -180,9 +170,7 @@ class AutomationOperatorSelector extends StatelessWidget {
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'or'));
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or'));
},
),
Container(
@ -208,9 +196,7 @@ class AutomationOperatorSelector extends StatelessWidget {
),
),
onPressed: () {
context
.read<RoutineBloc>()
.add(const ChangeAutomationOperator(operator: 'and'));
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and'));
},
),
],

View File

@ -8,211 +8,188 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() =>
_FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
class FetchRoutineScenesAutomation extends StatelessWidget
with HelperResponsiveLayout {
@override
void initState() {
super.initState();
}
const FetchRoutineScenesAutomation({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Scenes (Tab to Run)",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.scenes.isNotEmpty)
SizedBox(
height: 200,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) {
final scene = state.scenes[index];
final isLoading =
state.loadingSceneId == scene.id;
if (state.isLoading)
return const Center(child: CircularProgressIndicator());
return Padding(
padding: EdgeInsets.only(
right:
isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: Column(
children: [
RoutineViewCard(
isLoading: isLoading,
sceneOnTap: () {
context.read<RoutineBloc>().add(
SceneTrigger(
sceneId: scene.id,
name: scene.name));
},
status: state.scenes[index].status,
communityId:
state.scenes[index].communityId ??
'',
spaceId: state.scenes[index].spaceId,
sceneId:
state.scenes[index].sceneTuyaId!,
automationId: state.scenes[index].id,
cardType: 'scenes',
spaceName:
state.scenes[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context)
.add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId:
state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ??
Assets.logoHorizontal,
isFromScenes: true,
iconInBytes:
state.scenes[index].iconInBytes,
),
],
),
);
}),
),
const SizedBox(height: 10),
Text(
"Automations",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 3),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.automations.isNotEmpty)
SizedBox(
height: 200,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) {
final isLoading = state.automations!
.contains(state.automations[index].id);
return Column(
children: [
Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context)
? 4.0
: 8.0,
),
child: RoutineViewCard(
isLoading: isLoading,
onChanged: (v) {
context.read<RoutineBloc>().add(
UpdateAutomationStatus(
automationId: state
.automations[index].id,
automationStatusUpdate:
AutomationStatusUpdate(
spaceUuid: state
.automations[
index]
.spaceId,
isEnable: v),
communityId: state
.automations[index]
.communityId,
),
);
},
status: state.automations[index].status,
communityId: '',
spaceId:
state.automations[index].spaceId,
sceneId: '',
automationId:
state.automations[index].id,
cardType: 'automations',
spaceName:
state.scenes[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context)
.add(
const CreateNewRoutineViewEvent(
createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state
.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString:
state.automations[index].name,
icon: state.automations[index].icon ??
Assets.automation,
),
),
],
);
}),
),
],
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
_buildListTitle(context, "Scenes (Tab to Run)"),
const SizedBox(height: 10),
Visibility(
visible: state.scenes.isNotEmpty,
replacement: _buildEmptyState(context, "No scenes found"),
child: SizedBox(
height: 200,
child: _buildScenes(state),
),
),
);
const SizedBox(height: 10),
_buildListTitle(context, "Automations"),
const SizedBox(height: 3),
Visibility(
visible: state.automations.isNotEmpty,
replacement:
_buildEmptyState(context, "No automations found"),
child: SizedBox(
height: 200,
child: _buildAutomations(state),
),
)
],
),
),
);
},
);
}
Widget _buildAutomations(RoutineState state) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) {
final isLoading =
state.automations.contains(state.automations[index].id);
return Column(
children: [
Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
isLoading: isLoading,
onChanged: (v) {
context.read<RoutineBloc>().add(
UpdateAutomationStatus(
automationId: state.automations[index].id,
automationStatusUpdate: AutomationStatusUpdate(
spaceUuid: state.automations[index].spaceId,
isEnable: v,
),
communityId: state.automations[index].communityId,
),
);
},
status: state.automations[index].status,
communityId: '',
spaceId: state.automations[index].spaceId,
sceneId: '',
automationId: state.automations[index].id,
cardType: 'automations',
spaceName: state.automations[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true,
),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state.automations[index].id,
isAutomation: true,
isUpdate: true,
),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ?? Assets.automation,
),
),
],
);
},
);
}
Widget _buildScenes(RoutineState state) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) {
final scene = state.scenes[index];
final isLoading = state.loadingSceneId == scene.id;
return Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: Column(
children: [
RoutineViewCard(
isLoading: isLoading,
sceneOnTap: () {
context.read<RoutineBloc>().add(
SceneTrigger(
sceneId: scene.id,
name: scene.name,
),
);
},
status: state.scenes[index].status,
communityId: state.scenes[index].communityId,
spaceId: state.scenes[index].spaceId,
sceneId: state.scenes[index].sceneTuyaId!,
automationId: state.scenes[index].id,
cardType: 'scenes',
spaceName: state.scenes[index].spaceName,
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(
createRoutineView: true,
),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
],
),
);
});
}
Widget _buildListTitle(BuildContext context, String title) {
return Text(
title,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
);
}
Widget _buildEmptyState(BuildContext context, String title) {
return Padding(
padding: const EdgeInsets.only(bottom: 100),
child: Text(
title,
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
);
}
}

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -66,7 +67,6 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
@override
Widget build(BuildContext context) {
// Use widget.<mixinMethod> instead of just <mixinMethod>
final double cardWidth = widget.isSmallScreenSize(context)
? 120
: widget.isMediumScreenSize(context)
@ -121,29 +121,29 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
child: SizedBox(
width: 16,
height: 16,
child:
CircularProgressIndicator(strokeWidth: 2),
child: CircularProgressIndicator(strokeWidth: 2),
),
),
)
else
CupertinoSwitch(
activeColor: ColorsManager.primaryColor,
activeTrackColor: ColorsManager.primaryColor,
value: widget.status == 'enable',
onChanged: widget.onChanged,
)
],
)
: const SizedBox(),
InkWell(
onTap: widget.onTap,
child: Column(
children: [
Center(
Column(
children: [
Center(
child: InkWell(
customBorder: const CircleBorder(),
onTap: widget.onTap,
child: Container(
decoration: BoxDecoration(
color: ColorsManager.graysColor,
borderRadius: BorderRadius.circular(120),
shape: BoxShape.circle,
border: Border.all(
color: ColorsManager.greyColor,
width: 2.0,
@ -159,9 +159,8 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
height: iconSize,
width: iconSize,
fit: BoxFit.contain,
errorBuilder:
(context, error, stackTrace) =>
Image.asset(
errorBuilder: (context, error, stackTrace) =>
Image.asset(
Assets.logo,
height: iconSize,
width: iconSize,
@ -191,52 +190,48 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Column(
children: [
Text(
widget.textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize:
widget.isSmallScreenSize(context) ? 10 : 12,
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 3),
child: Column(
children: [
Text(
widget.textString,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: widget.isSmallScreenSize(context) ? 10 : 12,
),
if (widget.spaceName != '')
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.spaceLocationIcon,
fit: BoxFit.contain,
),
if (widget.spaceName != '')
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
Assets.spaceLocationIcon,
fit: BoxFit.contain,
),
Text(
widget.spaceName,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize:
widget.isSmallScreenSize(context) ? 10 : 12,
),
Text(
widget.spaceName,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 2,
style:
context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize:
widget.isSmallScreenSize(context)
? 10
: 12,
),
),
],
),
],
),
),
],
),
],
),
],
),
),
],
),
],
),

View File

@ -17,6 +17,8 @@ class _RoutineDevicesState extends State<RoutineDevices> {
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'};
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
@ -31,47 +33,41 @@ class _RoutineDevicesState extends State<RoutineDevices> {
}
});
final deviceList = state.devices.where((device) {
const allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
return allowedProductTypes.contains(device.productType);
}).toList();
final deviceList = state.devices
.where((device) => _allowedProductTypes.contains(device.productType))
.toList();
return Wrap(
spacing: 10,
runSpacing: 10,
children: deviceList.asMap().entries.map((entry) {
final device = entry.value;
final deviceData = {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
'tag': device.deviceTags!.isNotEmpty ? device.deviceTags![0].name : '',
'subSpace': device.deviceSubSpace?.subspaceName ?? '',
};
if (state.searchText != null && state.searchText!.isNotEmpty) {
return device.name!
.toLowerCase()
.contains(state.searchText!.toLowerCase())
return device.name!.toLowerCase().contains(state.searchText!.toLowerCase())
? DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
imagePath: deviceData['imagePath'] as String,
title: deviceData['title'] as String,
deviceData: deviceData,
)
: const SizedBox.shrink();
} else {
return DraggableCard(
imagePath: device.getDefaultIcon(device.productType),
title: device.name ?? '',
deviceData: {
'device': device,
'imagePath': device.getDefaultIcon(device.productType),
'title': device.name ?? '',
'deviceId': device.uuid,
'productType': device.productType,
'functions': device.functions,
'uniqueCustomId': '',
},
imagePath: deviceData['imagePath'] as String,
title: deviceData['title'] as String,
deviceData: deviceData,
);
}
}).toList(),

View File

@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class RoutineDialogFunctionListTile extends StatelessWidget {
const RoutineDialogFunctionListTile({
super.key,
required this.iconPath,
required this.operationName,
required this.onTap,
});
final String iconPath;
final String operationName;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return ListTile(
leading: SvgPicture.asset(
iconPath,
width: 24,
height: 24,
placeholderBuilder: (context) => const SizedBox(
width: 24,
height: 24,
),
),
title: Text(
operationName,
style: context.textTheme.bodyMedium,
),
trailing: const Icon(
Icons.arrow_forward_ios,
size: 16,
color: ColorsManager.textGray,
),
onTap: onTap,
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class RoutineDialogSelectionListTile extends StatelessWidget {
const RoutineDialogSelectionListTile({
required this.iconPath,
required this.description,
required this.isSelected,
required this.onTap,
super.key,
});
final bool isSelected;
final String iconPath;
final String description;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return ListTile(
leading: SvgPicture.asset(
iconPath,
width: 24,
height: 24,
placeholderBuilder: (context) => Container(
width: 24,
height: 24,
color: Colors.transparent,
),
),
title: Text(
description,
style: context.textTheme.bodyMedium,
),
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: onTap,
);
}
}

View File

@ -1,41 +1,51 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/helpers/routine_tap_function_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
class ACHelper {
static Future<Map<String, dynamic>?> showACFunctionsDialog(
BuildContext context,
List<DeviceFunction> functions,
AllDevicesModel? device,
List<DeviceFunctionData>? deviceSelectedFunctions,
String uniqueCustomId,
bool? removeComparetors,
) async {
List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
static Future<Map<String, dynamic>?> showACFunctionsDialog({
required BuildContext context,
required List<DeviceFunction> functions,
required AllDevicesModel? device,
required List<DeviceFunctionData>? deviceSelectedFunctions,
required String uniqueCustomId,
required bool? removeComparetors,
required String dialogType,
}) async {
List<ACFunction> acFunctions =
functions.whereType<ACFunction>().where((function) {
if (dialogType == 'THEN') {
return function.type == 'THEN' || function.type == 'BOTH';
}
return function.type == 'IF' || function.type == 'BOTH';
}).toList();
// List<ACFunction> acFunctions = functions.whereType<ACFunction>().toList();
return showDialog<Map<String, dynamic>?>(
context: context,
builder: (BuildContext context) {
return BlocProvider(
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
create: (_) => FunctionBloc()
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
child: AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData =
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
final selectedFunctionData = state.addedFunctions
.firstWhere((f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
@ -65,11 +75,26 @@ class ACHelper {
child: _buildFunctionsList(
context: context,
acFunctions: acFunctions,
onFunctionSelected: (functionCode, operationName) =>
context.read<FunctionBloc>().add(SelectFunction(
functionCode: functionCode,
operationName: operationName,
)),
device: device,
onFunctionSelected: (functionCode, operationName) {
RoutineTapFunctionHelper.onTapFunction(
context,
functionCode: functionCode,
functionOperationName: operationName,
functionValueDescription:
selectedFunctionData.valueDescription,
deviceUuid: device?.uuid,
codesToAddIntoFunctionsWithDefaultValue: [
'temp_set',
'temp_current',
],
defaultValue: functionCode == 'temp_set'
? 200
: functionCode == 'temp_current'
? -100
: 0,
);
},
),
),
// Value selector
@ -128,6 +153,7 @@ class ACHelper {
required BuildContext context,
required List<ACFunction> acFunctions,
required Function(String, String) onFunctionSelected,
required AllDevicesModel? device,
}) {
return ListView.separated(
shrinkWrap: false,
@ -180,8 +206,9 @@ class ACHelper {
required String operationName,
bool? removeComparators,
}) {
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
final initialValue = selectedFunctionData?.value ?? 250;
final initialValue = selectedFunctionData?.value ?? initialVal;
return _buildTemperatureSelector(
context: context,
initialValue: initialValue,
@ -275,7 +302,9 @@ class ACHelper {
functionCode: selectCode,
operationName: operationName,
condition: conditions[index],
value: selectedFunctionData?.value,
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
? 200
: -100,
valueDescription: selectedFunctionData?.valueDescription,
),
),
@ -304,6 +333,7 @@ class ACHelper {
DeviceFunctionData? selectedFunctionData,
String selectCode,
) {
final initialVal = selectCode == 'temp_set' ? 200 : -100;
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
@ -311,7 +341,7 @@ class ACHelper {
borderRadius: BorderRadius.circular(10),
),
child: Text(
'${(initialValue ?? 200) / 10}°C',
'${(initialValue ?? initialVal) / 10}°C',
style: context.textTheme.headlineMedium!.copyWith(
color: ColorsManager.primaryColorWithOpacity,
),
@ -386,7 +416,9 @@ class ACHelper {
trailing: Icon(
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
size: 24,
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
color: isSelected
? ColorsManager.primaryColorWithOpacity
: ColorsManager.textGray,
),
onTap: () {
if (!isSelected) {

View File

@ -0,0 +1,224 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
class CeilingSensorDialog extends StatefulWidget {
const CeilingSensorDialog({
required this.uniqueCustomId,
required this.functions,
required this.deviceSelectedFunctions,
required this.device,
required this.dialogType,
super.key,
});
final String? uniqueCustomId;
final List<DeviceFunction> functions;
final List<DeviceFunctionData> deviceSelectedFunctions;
final AllDevicesModel? device;
final String dialogType;
@override
State<CeilingSensorDialog> createState() => _CeilingSensorDialogState();
}
class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
late final List<CpsFunctions> _cpsFunctions;
late final String _dialogHeaderText;
@override
void initState() {
super.initState();
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
if (widget.dialogType == 'THEN') {
return function.type == 'THEN' || function.type == 'BOTH';
}
return function.type == 'IF' || function.type == 'BOTH';
}).toList();
final isIfDialog = widget.dialogType == 'IF';
_dialogHeaderText = isIfDialog ? 'Conditions' : 'Functions';
}
@override
Widget build(BuildContext context) {
return AlertDialog(
contentPadding: EdgeInsets.zero,
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
builder: (context, state) {
final selectedFunction = state.selectedFunction;
return Container(
width: selectedFunction != null ? 600 : 360,
height: 450,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.only(top: 20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DialogHeader('Presence Sensor $_dialogHeaderText'),
Expanded(child: _buildMainContent(context, state)),
DialogFooter(
onCancel: () => Navigator.pop(context),
onConfirm: state.addedFunctions.isNotEmpty
? () {
final functions = _updateValuesForAddedFunctions(
state.addedFunctions,
);
context.read<RoutineBloc>().add(
AddFunctionToRoutine(
functions,
'${widget.uniqueCustomId}',
),
);
Navigator.pop(context, {
'deviceId': widget.functions.first.deviceId,
});
}
: null,
isConfirmEnabled: selectedFunction != null,
),
],
),
);
},
),
);
}
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
final selectedFunction = state.selectedFunction;
final selectedOperationName = state.selectedOperationName;
final selectedFunctionData = state.addedFunctions.firstWhere(
(f) => f.functionCode == selectedFunction,
orElse: () => DeviceFunctionData(
entityId: '',
functionCode: selectedFunction ?? '',
operationName: '',
value: null,
),
);
final selectedCpsFunctions = _cpsFunctions.firstWhere(
(f) => f.code == selectedFunction,
orElse: () => CpsMovementFunctions(
deviceId: '',
deviceName: '',
type: '',
),
);
final operations = selectedCpsFunctions.getOperationalValues();
final isSensitivityFunction = selectedFunction == 'sensitivity';
final isToggleFunction = isSensitivityFunction
? widget.dialogType == 'THEN'
: CeilingSensorHelper.toggleCodes.contains(selectedFunction);
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
CpsFunctionsList(
cpsFunctions: _cpsFunctions,
device: widget.device,
selectedFunctionData: selectedFunctionData,
dialogType: widget.dialogType,
),
if (state.selectedFunction != null)
Expanded(
child: isToggleFunction
? CpsDialogValueSelector(
operations: operations,
selectedFunction: selectedFunction ?? '',
selectedFunctionData: selectedFunctionData,
cpsFunctions: _cpsFunctions,
operationName: selectedOperationName ?? '',
device: widget.device,
)
: CpsDialogSliderSelector(
operations: operations,
selectedFunction: selectedFunction ?? '',
selectedFunctionData: selectedFunctionData,
cpsFunctions: _cpsFunctions,
operationName: selectedOperationName ?? '',
device: widget.device,
dialogType: widget.dialogType,
),
),
],
);
}
static const _mappableSteppedFunctions = <String>{
'static_max_dis',
'presence_reference',
'moving_reference',
'perceptual_boundary',
'moving_boundary',
'moving_rigger_time',
'moving_static_time',
'none_body_time',
'moving_max_dis',
'moving_range',
'presence_range',
};
List<DeviceFunctionData> _updateValuesForAddedFunctions(
List<DeviceFunctionData> addedFunctions,
) {
return addedFunctions.map((function) {
final shouldMapValue = _mappableSteppedFunctions.contains(
function.functionCode,
);
if (shouldMapValue) {
final mappedValue = _mapSteppedValue(
value: function.value,
inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode),
inputRange: CpsSliderHelpers.sliderRange(function.functionCode),
outputRange: CpsSliderHelpers.mappedRange(function.functionCode),
);
return DeviceFunctionData(
value: mappedValue,
entityId: function.entityId,
functionCode: function.functionCode,
operationName: function.operationName,
condition: function.condition,
actionExecutor: function.actionExecutor,
valueDescription: function.valueDescription,
);
}
return function;
}).toList();
}
int _mapSteppedValue({
required (double min, double max) inputRange,
required double inputStep,
required (double min, double max, double dividend) outputRange,
required double value,
}) {
final (inputMin, inputMax) = inputRange;
final (outputMin, outputMax, outputStep) = outputRange;
final clampedValue = value.clamp(inputMin, inputMax);
final stepsFromMin = ((clampedValue - inputMin) / inputStep).round();
final mappedValue = outputMin + (stepsFromMin * outputStep);
return mappedValue.clamp(outputMin, outputMax).round();
}
}

View File

@ -0,0 +1,176 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart';
abstract final class CeilingSensorHelper {
const CeilingSensorHelper._();
static Future<Map<String, dynamic>?> showCeilingSensorDialog({
required BuildContext context,
required String? uniqueCustomId,
required List<DeviceFunction> functions,
required List<DeviceFunctionData> deviceSelectedFunctions,
required AllDevicesModel? device,
required String dialogType,
}) {
return showDialog(
context: context,
builder: (context) => BlocProvider(
create: (context) => FunctionBloc()
..add(
InitializeFunctions(deviceSelectedFunctions),
),
child: CeilingSensorDialog(
uniqueCustomId: uniqueCustomId,
functions: functions,
deviceSelectedFunctions: deviceSelectedFunctions,
device: device,
dialogType: dialogType,
),
),
);
}
static List<DeviceFunction<CpsOperationalValue>> getCeilingSensorFunctions({
required String uuid,
required String name,
}) {
return [
CpsRadarSwitchFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsSpatialParameterSwitchFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsSensitivityFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMovingSpeedFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsSpatialStaticValueFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsSpatialMotionValueFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsMaxDistanceOfDetectionFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMaxDistanceOfStaticDetectionFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsDetectionRangeFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsDistanceOfMovingObjectsFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsPresenceJudgementThrsholdFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMotionAmplitudeTriggerThresholdFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsPerpetualBoundaryFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMotionTriggerBoundaryFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMotionTriggerTimeFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMotionToStaticTimeFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsEnteringNoBodyStateTimeFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsSelfTestResultFunctions(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsNobodyTimeFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsMovementFunctions(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsCustomModeFunction(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsSpaceTypeFunctions(
deviceName: name,
deviceId: uuid,
type: 'BOTH',
),
CpsPresenceStatusFunctions(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
CpsSportsParaFunction(
deviceName: name,
deviceId: uuid,
type: 'IF',
),
];
}
static const toggleCodes = <String>{
'radar_switch',
'space_para_switch',
'checking_result',
'nobody_time',
'body_movement',
'custom_mode',
'scene',
'presence_state',
};
}

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