Compare commits

..

164 Commits

Author SHA1 Message Date
6c691d4b3c displays ceiling sensor in routine 2025-04-09 12:46:43 +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
d08ab8caac add dialogType to devices and add parameter in showSwitchFunctionsDialog 2025-04-09 10:09:56 +03:00
c2476b9719 Merge pull request #132 from SyncrowIOT/fix_routine_popup_and_wall_sensor_bugs
Fix routine popup and wall sensor bugs
2025-04-07 16:43:48 +03:00
a56f4e488e Refactor routine_view_card.dart to adjust the size of the CircularProgressIndicator
Fix routine popup
Update wall_presence_sensor.dart to handle null selectedOperationName
2025-04-07 16:42:44 +03:00
6bd9fb7e4e Refactor routine_view_card.dart to adjust the size o 2025-04-07 16:22:21 +03:00
d264409d29 - Refactor the WpsFunctions class in wps_functions.dart to use 'cm' instead of 'temp' for the description of operational values.
- Update the WallPresenceSensor class in wall_presence_sensor.dart to use the selected operation name
2025-04-07 14:27:36 +03:00
ca44f3bf55 fix routine popup 2025-04-07 12:53:12 +03:00
9949a0a0bf Merge pull request #131 from SyncrowIOT/SP-1281-FE-Save-Display-Configured-Sensor-as-a-Card
save presence state and edit selected value
2025-04-07 09:30:38 +03:00
8dc4081a89 save presence state and edit selected value 2025-04-06 16:38:34 +03:00
52498f4e6b Merge pull request #130 from SyncrowIOT/routine_ui_issue
Refactor SVG icons and update asset paths and fix RoutinesView issues
2025-04-06 16:33:49 +03:00
37440f288c Merge branch 'dev' into routine_ui_issue 2025-04-06 16:27:34 +03:00
973ae82bec Merge pull request #125 from SyncrowIOT/SP-1271-rework
SP-1271-rework
2025-04-06 11:59:46 +03:00
8c4dec4483 Merge pull request #129 from SyncrowIOT/SP-1279
add WallPresenceSensor and add new icons for presence state and selec…
2025-04-06 11:57:54 +03:00
ad922577da Refactor SVG icons and update asset paths and fix RoutinesView issues 2025-04-06 11:28:59 +03:00
49c743befb remove debug print statements 2025-04-06 10:04:36 +03:00
4fc259491e add WallPresenceSensor and add new icons for presence state and selected value 2025-04-06 09:57:12 +03:00
64f61a6c3d Merged with dev 2025-04-06 01:17:30 +03:00
ab3f268f29 Added pagination and search logic in space tree 2025-04-06 01:14:16 +03:00
b345822366 Merge pull request #128 from SyncrowIOT:SP-1200
fixing duplicate issue
2025-04-03 10:50:31 +04:00
76793a3c0b Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1200 2025-04-03 10:48:56 +04:00
c330f11206 Merge pull request #127 from SyncrowIOT/SP-1246
updated endpoints for devices
2025-04-03 10:23:34 +04:00
9cb62795a6 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1246 2025-04-03 10:17:06 +04:00
a034202d76 updated endpoints 2025-04-03 10:14:24 +04:00
869799f08e Merge pull request #126 from SyncrowIOT/fix_routine_popup
Refactor routine creation UI for improved code organization
2025-03-27 15:06:38 +03:00
573e7b42ed Refactor routine creation UI for improved code organization 2025-03-27 15:04:33 +03:00
22935f7007 SP-1271-rework 2025-03-27 14:52:26 +03:00
5bf5120883 Merge pull request #124 from SyncrowIOT/SP-1271-Fix-Deployment
SP-1271-Fix-Deployment
2025-03-27 13:45:59 +03:00
f307aaff9d Removed the usage of spacing property because the GitHub action doesnt support the version that has it, so instead opted for using Padding. 2025-03-27 11:08:57 +03:00
aea18fb293 Merge pull request #123 from SyncrowIOT/SP-1271-FE-Remove-Community-Field-from-Device-Management-Screen
Sp 1271 fe remove community field from device management screen
2025-03-26 12:57:16 +03:00
835dfe8785 Rename controller variables in DeviceSearchFilters to be private to control access control. 2025-03-26 12:40:25 +03:00
f670ae78aa Refactor device search filters layout for improved code organization and to remove any code duplication. 2025-03-26 12:39:51 +03:00
d3128a9c9c Refactor search button callback for improved readability 2025-03-26 12:37:03 +03:00
b65f172f9d Removed memory leak bug from DeviceSearchFilters widget. 2025-03-26 12:36:26 +03:00
dad18b77de Removed community filter field from DeviceSearchFilters. 2025-03-26 12:35:04 +03:00
77a9aa2f19 Added include spaces to the communites api 2025-03-26 04:29:33 +03:00
b77dc860cb Merge pull request #122 from SyncrowIOT/SP-1106
Sp 1106
2025-03-24 15:34:05 +03:00
6045158432 Reset Selected space 2025-03-24 15:32:02 +03:00
7e06dd76cf create routine dialog flow 2025-03-24 13:57:53 +03:00
7e1c2ba712 routines and automation Toggle 2025-03-21 17:14:05 +03:00
80c43b8046 Merge pull request #121 from SyncrowIOT:bugfix/fix-subspace-update
fixed issue in subspace update
2025-03-19 21:49:51 +04:00
2d3345c1d9 fixed issue in subspace updatw 2025-03-19 21:48:44 +04:00
de5030d9f0 Merge branch 'dev' into SP-1200 2025-03-19 14:12:13 +04:00
c0d53fdf5c Update Automation Status 2025-03-19 12:24:55 +03:00
8c15fffd42 duplicate 2025-03-19 12:25:37 +04:00
5e05b20f68 Merge pull request #118 from SyncrowIOT/SP-1318
Sp 1318
2025-03-17 09:34:16 +04:00
95730cc2ba Merge pull request #119 from SyncrowIOT/SP-1327
append space id to list
2025-03-17 09:33:28 +04:00
3750fa8329 removed left and right button 2025-03-14 15:02:22 +04:00
a5a37f3841 append space id to list 2025-03-14 14:10:03 +04:00
e0486deecb changed endpoint for automation update 2025-03-14 12:16:32 +04:00
bc64efa557 updated delete automation endpoint 2025-03-14 12:12:15 +04:00
2a065efc0e replaced endpoint for get automaiton by id 2025-03-14 12:09:14 +04:00
7ae3e4a933 updated endpoint for creating automations 2025-03-14 11:54:47 +04:00
2a0f6a4596 updated endpoint of get automation by space 2025-03-14 11:50:18 +04:00
5b2822f973 Merge pull request #117 from SyncrowIOT/SP-1318
Sp 1318
2025-03-13 14:51:54 +04:00
70d31f5351 removed print 2025-03-13 14:51:09 +04:00
c39c693755 remove unused import 2025-03-13 14:37:39 +04:00
8ed6980264 resposnive text length of space model name 2025-03-13 13:51:52 +04:00
16df75eb49 fixed space update 2025-03-13 13:14:31 +04:00
80c294f09c fix real time listenToChanges 2025-03-12 16:03:01 +03:00
c806b5f59d Added a background color for the space tree 2025-03-11 23:54:39 +03:00
dd01a7fddb Pulled latest changes 2025-03-11 23:42:42 +03:00
b563cc378e Added current temp to AC functions 2025-03-11 23:41:21 +03:00
2140b7eee3 Merge pull request #114 from SyncrowIOT:bugfix/update-space
update space with a space model
2025-03-11 22:08:06 +04:00
7de9e25ed5 Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/update-space 2025-03-11 22:07:50 +04:00
123949aa86 update space with a space model 2025-03-11 22:05:36 +04:00
6d554c5953 Merge pull request #113 from SyncrowIOT/bugfix/remove-link
fixed issue in removing link
2025-03-10 16:55:02 +04:00
5ed87a5794 fixed issue in removing link 2025-03-10 11:47:00 +04:00
168c997240 Merge pull request #112 from SyncrowIOT:bugfix/empty-space-model
fixed issue in update
2025-03-09 17:32:38 +04:00
de65f79ccf Merge branch 'dev' of https://github.com/SyncrowIOT/web into bugfix/empty-space-model 2025-03-09 17:32:06 +04:00
eb0490fb16 fixed issue in update 2025-03-09 17:31:36 +04:00
a73fc04712 Merge pull request #110 from SyncrowIOT/SP-1297
fix model and SpaceModelCardWidget and LinkSpaceModelSpacesDialog
2025-03-09 16:11:18 +03:00
dee07ebb06 fix LinkingSuccessful 2025-03-09 16:01:04 +03:00
5237f3ae5b Merge pull request #111 from SyncrowIOT/bugfix/empty-space-model
fix the circular progress on empty space model
2025-03-09 16:49:36 +04:00
e017633b9b fixed space model 2025-03-09 16:48:42 +04:00
e8e5e9bcb7 fix LoadedSpaceView 2025-03-09 14:54:13 +03:00
a1d15c9cea fix the circular progress on empty space model 2025-03-09 15:53:55 +04:00
16c006e7a9 Merge pull request #109 from SyncrowIOT/feat/fix-update-on-community-tree
Feat/fix-update-on-community-tree
2025-03-09 14:06:48 +04:00
24280549ec revert back color change 2025-03-09 14:06:33 +04:00
8359642a1a Merge branch 'dev' into SP-1297 2025-03-09 12:03:34 +03:00
ada19a5992 fetch communities and spaces on space model update 2025-03-09 10:47:09 +04:00
c6702d4d5f Merge branch 'dev' of https://github.com/SyncrowIOT/web into dev 2025-03-09 10:29:05 +04:00
f78e2e2d36 Merge branch 'feat/update-space-model' of https://github.com/SyncrowIOT/web into dev 2025-03-09 10:26:34 +04:00
83b9555920 added padding to space tree on community structure 2025-03-09 10:26:30 +04:00
8af24e575c fix model and SpaceModelCardWidget and LinkSpaceModelSpacesDialog 2025-03-08 09:57:35 +03:00
4301d7f62f Merge pull request #108 from SyncrowIOT/feat/update-space-model
Feat/update space model
2025-03-07 11:20:29 +04:00
788d36541b revert settings.json 2025-03-07 11:19:46 +04:00
44cf1cec69 resolved conflict 2025-03-07 11:18:36 +04:00
acb7ed5192 Merge branch 'dev' of https://github.com/SyncrowIOT/web into feat/update-space-model 2025-03-07 00:30:13 +04:00
1b8a87e942 initcommunity on save space 2025-03-07 00:01:35 +04:00
fa39182386 Merge pull request #106 from SyncrowIOT/link_space_model_spaces
Link space model spaces
2025-03-06 12:54:38 +03:00
54a250ea2f remove unused code 2025-03-06 12:51:30 +03:00
efccac4d19 removed unused widget 2025-03-06 13:29:28 +04:00
096317fce8 Merge branch 'dev' of https://github.com/SyncrowIOT/web into feat/update-space-model 2025-03-06 13:19:53 +04:00
254485235d Merge branch 'dev' into link_space_model_spaces 2025-03-06 12:08:54 +03:00
d624dd767b add tags 2025-03-06 12:52:41 +04:00
add34b327f space model changes 2025-03-06 11:50:47 +03:00
d75ac416eb Merge branch 'dev' into link_space_model_spaces 2025-03-06 11:50:21 +03:00
048a7c0e8f change model name 2025-03-06 11:10:53 +03:00
c8aab320df Merge pull request #107 from SyncrowIOT/SP-1218
Change in space management side panel
2025-03-06 12:02:12 +04:00
9795517a3f Merge branch 'SP-1218' into feat/update-space-model 2025-03-06 12:01:12 +04:00
60bf58d5fd Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1218 2025-03-06 11:51:13 +04:00
8d072ee6bb added left padding 2025-03-06 11:48:43 +04:00
de614d40a5 removed unused import 2025-03-06 11:43:18 +04:00
45f6599c40 removed logs 2025-03-06 11:42:28 +04:00
fecab17cbe updated body for update space model 2025-03-06 11:41:36 +04:00
d2ff909bf2 updated tag dialogue selection 2025-03-05 22:24:45 +04:00
5cea8eddb3 added project tags to all dialogs 2025-03-05 21:26:36 +04:00
213ec329c0 passed tags to space model page 2025-03-05 17:16:00 +04:00
c9427b35be added tags 2025-03-05 17:12:02 +04:00
a7995bb2b8 added tag api 2025-03-05 16:59:58 +04:00
20474835dd Merge branch 'dev' of https://github.com/SyncrowIOT/web into feat/update-space-model 2025-03-05 16:42:43 +04:00
5233c1b38e format settings 2025-03-05 16:41:57 +04:00
ec502214f4 Merge pull request #104 from SyncrowIOT/connect_real_time
Connect real time
2025-03-05 15:24:08 +03:00
215dd9cfa4 link space model api integration 2025-03-05 14:37:52 +03:00
dcfbe5282e add dev and prod firebase option 2025-03-05 14:21:07 +03:00
d88ae9ea15 removed tag model and use tag only 2025-03-05 14:19:58 +04:00
64adf516f6 revert back 2025-03-05 12:28:53 +04:00
ef777cfce9 remove unused import 2025-03-05 12:28:16 +04:00
6e90f81760 update space bloc on community and space change 2025-03-05 12:28:00 +04:00
51cfa8d5ae cached space models 2025-03-05 12:21:47 +04:00
4469efe465 cached space model 2025-03-04 20:51:21 +04:00
c7c8e9a519 cached space models 2025-03-04 11:40:06 +04:00
6f2e2e2d4a Check all the devices working with real time 2025-03-02 17:55:26 +03:00
692b05a600 added update in space tree bloc 2025-03-02 14:40:34 +04:00
7607e5f80d reduced no of calls on load 2025-02-28 19:37:50 +04:00
b84513c09d removed checkbox 2025-02-28 11:51:48 +04:00
41605bef6b Merge pull request #102 from SyncrowIOT/sp_1171
Added space tree to the add user dialog
2025-02-27 12:32:53 +03:00
fd24d6bd27 change community and spaces in user manage 2025-02-27 12:29:12 +03:00
fe52726f6e WebAppBar ResponsiveLayout 2025-02-26 12:00:30 +03:00
5ea29efaf5 Merge pull request #103 from SyncrowIOT/SP-1151-FE-Implement-Delete-Action-for-the-Space-Model
Sp 1151 fe implement delete action for the space model
2025-02-26 10:14:06 +04:00
70cb12236b Clear cache on edit dialog and fixed a selection issue 2025-02-26 02:59:17 +03:00
9f75ce817e added space model delete to bloc and api 2025-02-25 21:31:40 +04:00
aa23daa0b4 added DeleteSpaceModelDialog into SpaceModelCardWidget for delete 2025-02-25 21:31:25 +04:00
61f0c3ad2b DeleteSpaceModelDialog into a separate widget 2025-02-25 21:30:27 +04:00
553c77d1e3 Added space tree to the add user dialog 2025-02-25 00:55:44 +03:00
27fef7ddaa Pulled latest changes 2025-02-20 12:55:07 +03:00
010176cc25 Added clear data and cached events in space tree 2025-02-20 12:53:59 +03:00
d08a1d1037 Merge pull request #100 from SyncrowIOT/bugfix/fix-get-all-device-endpoint
fixed device endpoint
2025-02-20 13:33:55 +04:00
e634154fb3 fixed device endpoint 2025-02-20 13:31:31 +04:00
c0f59aba61 Merge pull request #98 from SyncrowIOT:SP-1186-FE-Cursor-Resets-After-Each-Character-When-Creating-a-Tag
fixed text theme of tag list
2025-02-20 11:48:36 +04:00
7ee7681e09 fixed theme 2025-02-20 11:47:58 +04:00
57c5f4752c fixed theme 2025-02-20 11:36:08 +04:00
ad4f2ae382 Merge pull request #96 from SyncrowIOT/SP-1186-FE-Cursor-Resets-After-Each-Character-When-Creating-a-Tag
fixed cursor issue
2025-02-20 11:29:03 +04:00
eafb811d2e fixed cursor issue 2025-02-20 11:26:30 +04:00
2d0dcc41df Merge pull request #93 from SyncrowIOT/bugfix/fix-get-user-endpoint
change endpoint
2025-02-19 17:54:19 +04:00
d89e3fac8e change endpoint 2025-02-19 17:53:20 +04:00
25acd67351 Bug fixes in the side tree 2025-02-19 02:13:14 +03:00
b134823551 SpaceModelSelectedIds 2025-02-18 16:56:10 +03:00
1054970a63 validation LinkSpaceModelSpacesDialog 2025-02-18 16:34:29 +03:00
7cab299a51 Merge branch 'dev1' into link_space_model_spaces 2025-02-18 14:32:47 +03:00
a3c5670fee Merge branch 'dev' into link_space_model_spaces 2025-02-18 13:33:04 +03:00
009ede7d08 overwrite dialog and progress indicator and success dialog 2025-02-18 12:37:00 +03:00
0c2a092f4d Added side tree to space model 2025-02-18 12:31:43 +03:00
b968b5a6eb Merge pull request #92 from SyncrowIOT/bugfix/change-project-cubit
Bugfix/change-project-cubit
2025-02-18 13:22:12 +04:00
05edc7641a removed project cubit from all classes 2025-02-18 12:56:51 +04:00
b8204f1015 changed project class to use shared preference 2025-02-18 12:56:25 +04:00
d87fec796b Merge pull request #91 from SyncrowIOT/add_space_name
spaceName
2025-02-17 12:56:47 +03:00
b00b0c82dc spaceName 2025-02-17 12:36:04 +03:00
0aa029a2fc Removed static space id and community id in the routine 2025-02-17 03:32:40 +03:00
dec3a25639 Merge pull request #90 from SyncrowIOT/feat/use-project-uuid-instead-of-hardcode
Feat/use project UUID instead of hardcode
2025-02-17 01:11:53 +03:00
ba44d1d359 firebase configuration and connect device with real-time bd 2025-02-13 14:07:29 +03:00
00a9cb1188 link_space model 2025-02-10 12:38:20 +03:00
212 changed files with 8820 additions and 3035 deletions

View File

@ -1,5 +1,9 @@
plugins {
id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
// END: FlutterFire Configuration
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}

View File

@ -0,0 +1,68 @@
{
"project_info": {
"project_number": "427332280600",
"firebase_url": "https://test2-8a3d2-default-rtdb.firebaseio.com",
"project_id": "test2-8a3d2",
"storage_bucket": "test2-8a3d2.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:550f67441246cb1a0c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow.app"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:bb6047adeeb80fb00c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow_application"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:427332280600:android:2bc36fbe82994a3e0c7e6d",
"android_client_info": {
"package_name": "com.example.syncrow_web"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -20,6 +20,10 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}

View File

@ -0,0 +1,18 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.8326 16.8575C7.55614 16.8575 4.89062 14.192 4.89062 10.9156C4.89062 7.63915 7.55614 4.97363 10.8326 4.97363C14.1091 4.97363 16.7746 7.63915 16.7746 10.9156C16.7746 14.192 14.1091 16.8575 10.8326 16.8575Z" fill="#FFE645"/>
<path d="M10.8333 4.97363C10.8332 4.97363 10.833 4.97363 10.833 4.97363V16.8575H10.8333C14.1098 16.8575 16.7753 14.192 16.7753 10.9156C16.7753 7.63915 14.1098 4.97363 10.8333 4.97363Z" fill="#FFF1B9"/>
<path d="M10.833 21.8334C10.4824 21.8334 10.1982 21.5493 10.1982 21.1986V18.8289C10.1982 18.4784 10.4824 18.1941 10.833 18.1941C11.1836 18.1941 11.4678 18.4784 11.4678 18.8289V21.1986C11.4678 21.5493 11.1836 21.8334 10.833 21.8334Z" fill="#FFE645"/>
<path d="M21.0317 11.6348H18.7461C18.3955 11.6348 18.1113 11.3506 18.1113 11C18.1113 10.6496 18.3955 10.3652 18.7461 10.3652H21.0317C21.3824 10.3652 21.6665 10.6496 21.6665 11C21.6665 11.3506 21.3824 11.6348 21.0317 11.6348Z" fill="#FFCF2C"/>
<path d="M2.92042 11.6348H0.634766C0.284157 11.6348 0 11.3506 0 11C0 10.6496 0.284157 10.3652 0.634766 10.3652H2.92042C3.27103 10.3652 3.55518 10.6496 3.55518 11C3.55518 11.3506 3.27103 11.6348 2.92042 11.6348Z" fill="#FFE645"/>
<path d="M17.7243 7.65695C17.5049 7.65695 17.2915 7.54306 17.174 7.33941C16.9988 7.03591 17.1027 6.64761 17.4064 6.47239L19.3481 5.3513C19.6517 5.17591 20.04 5.28005 20.2152 5.58372C20.3905 5.88721 20.2865 6.27551 19.9828 6.45073L18.0412 7.57182C17.9412 7.62951 17.8321 7.65695 17.7243 7.65695Z" fill="#FFCF2C"/>
<path d="M2.03686 16.7139C1.8175 16.7139 1.60409 16.6 1.48656 16.3963C1.31117 16.0928 1.41531 15.7045 1.71881 15.5293L3.69865 14.3862C4.00231 14.2108 4.39045 14.315 4.56567 14.6186C4.74106 14.9221 4.63708 15.3104 4.33342 15.4856L2.35358 16.6287C2.25373 16.6864 2.14447 16.7139 2.03686 16.7139Z" fill="#FFE645"/>
<path d="M5.76902 20.4051C5.6614 20.4051 5.5523 20.3778 5.4523 20.32C5.14863 20.1448 5.04466 19.7565 5.21988 19.453L6.36279 17.4735C6.53801 17.17 6.92631 17.0658 7.22981 17.2412C7.53347 17.4164 7.63744 17.8047 7.46222 18.1082L6.31931 20.0877C6.20178 20.2914 5.98838 20.4051 5.76902 20.4051Z" fill="#FFE645"/>
<path d="M3.94197 7.65681C3.83419 7.65681 3.72509 7.62937 3.62524 7.57168L1.64557 6.42877C1.34207 6.25355 1.23793 5.86525 1.41332 5.56158C1.58854 5.25809 1.97667 5.15395 2.28033 5.32933L4.26001 6.47224C4.56351 6.64746 4.66748 7.03576 4.49226 7.33926C4.37473 7.54291 4.16132 7.65681 3.94197 7.65681Z" fill="#FFE645"/>
<path d="M19.6649 16.7342C19.5572 16.7342 19.4481 16.7067 19.348 16.6491L17.333 15.4857C17.0295 15.3103 16.9255 14.9221 17.1007 14.6185C17.276 14.315 17.6643 14.2108 17.9678 14.3862L19.9828 15.5496C20.2865 15.7248 20.3905 16.1131 20.2152 16.4166C20.0977 16.6203 19.8843 16.7342 19.6649 16.7342Z" fill="#FFCF2C"/>
<path d="M15.8973 20.4051C15.6778 20.4051 15.4645 20.2913 15.347 20.0877L14.2043 18.1085C14.029 17.8048 14.133 17.4167 14.4367 17.2415C14.7402 17.0661 15.1285 17.1701 15.3037 17.4737L16.4464 19.4529C16.6217 19.7566 16.5177 20.1447 16.214 20.3199C16.114 20.3778 16.0049 20.4051 15.8973 20.4051Z" fill="#FFCF2C"/>
<path d="M10.833 3.72144C10.4824 3.72144 10.1982 3.43728 10.1982 3.08667V0.801514C10.1982 0.450905 10.4824 0.166748 10.833 0.166748C11.1836 0.166748 11.4678 0.450905 11.4678 0.801514V3.08667C11.4678 3.43728 11.1836 3.72144 10.833 3.72144Z" fill="#FFE645"/>
<path d="M14.7534 4.75952C14.6458 4.75952 14.5365 4.73208 14.4367 4.67422C14.133 4.499 14.029 4.11086 14.2043 3.8072L15.3472 1.82786C15.5224 1.5242 15.9105 1.42005 16.2142 1.59544C16.5178 1.77066 16.6218 2.15896 16.4466 2.46262L15.3037 4.44197C15.1862 4.64562 14.9728 4.75952 14.7534 4.75952Z" fill="#FFCF2C"/>
<path d="M6.91292 4.75919C6.69356 4.75919 6.48015 4.64529 6.36262 4.44164L5.21988 2.46262C5.04466 2.15896 5.14863 1.77066 5.4523 1.59544C5.75579 1.42005 6.14409 1.5242 6.31931 1.82786L7.46206 3.80687C7.63728 4.11053 7.5333 4.49867 7.22964 4.67405C7.1298 4.73175 7.02053 4.75919 6.91292 4.75919Z" fill="#FFE645"/>
<path d="M10.8333 18.1941C10.8332 18.1941 10.833 18.1941 10.833 18.1941V21.8334H10.8333C11.1839 21.8334 11.4681 21.5493 11.4681 21.1986V18.8289C11.4681 18.4784 11.1839 18.1941 10.8333 18.1941Z" fill="#FFCF2C"/>
<path d="M10.8333 0.166748C10.8332 0.166748 10.833 0.166748 10.833 0.166748V3.72144H10.8333C11.1839 3.72144 11.4681 3.43728 11.4681 3.08667V0.801514C11.4681 0.450905 11.1839 0.166748 10.8333 0.166748Z" fill="#FFCF2C"/>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,12 @@
<svg width="16" height="22" viewBox="0 0 16 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.3977 14.7672V18.2386C15.3977 19.0611 14.7285 19.7299 13.906 19.7299H1.49132C0.668788 19.7299 0 19.0611 0 18.2386V14.7672C0 10.522 3.45363 7.06836 7.69885 7.06836H7.73063C11.9615 7.08575 15.3977 10.5326 15.3977 14.7672Z" fill="#FF5B5B"/>
<path d="M15.3975 14.7672V18.2386C15.3975 19.0611 14.7283 19.7299 13.9058 19.7299H7.73047V7.06836C11.9613 7.08575 15.3975 10.5326 15.3975 14.7672Z" fill="#FF193D"/>
<path d="M15.3977 15.5405V18.239C15.3977 19.0615 14.7285 19.7303 13.906 19.7303H1.49132C0.668788 19.7303 0 19.0615 0 18.239V15.5405H15.3977Z" fill="#7C8287"/>
<path d="M15.3977 21.1981C15.3977 21.5492 15.1131 21.8334 14.7624 21.8334H0.635315C0.284205 21.8334 0 21.5492 0 21.1981C0 20.8474 0.284205 20.5627 0.635315 20.5627H14.7623C15.1131 20.5627 15.3977 20.8473 15.3977 21.1981Z" fill="#DCE6EB"/>
<path d="M8.36829 0.802063V4.67286C8.36829 5.02355 8.08366 5.30817 7.73297 5.30817C7.38228 5.30817 7.09766 5.02355 7.09766 4.67286V0.802063C7.09761 0.451377 7.38224 0.166748 7.73297 0.166748C8.0837 0.166748 8.36829 0.451377 8.36829 0.802063Z" fill="#FFB64C"/>
<path d="M2.87856 6.65271C2.67031 6.65271 2.4663 6.55047 2.34472 6.36262L0.357991 3.29471C0.167266 3.00023 0.251393 2.60684 0.545924 2.41612C0.840412 2.22535 1.23376 2.30952 1.42452 2.60405L3.41125 5.67195C3.60198 5.96644 3.51785 6.35983 3.22332 6.55055C3.11659 6.61966 2.99688 6.65271 2.87856 6.65271Z" fill="#FFB64C"/>
<path d="M12.3087 6.6527C12.1903 6.6527 12.0706 6.61965 11.9639 6.55054C11.6694 6.35982 11.5852 5.96643 11.7759 5.67194L13.7627 2.60404C13.9534 2.30947 14.3467 2.22538 14.6413 2.41611C14.9358 2.60683 15.0199 3.00022 14.8292 3.29471L12.8425 6.36261C12.7209 6.55042 12.5169 6.6527 12.3087 6.6527Z" fill="#FF9F00"/>
<path d="M15.3975 21.1981C15.3975 21.5492 15.1129 21.8334 14.7622 21.8334H7.73047V20.5627H14.7622C15.1129 20.5627 15.3975 20.8473 15.3975 21.1981Z" fill="#C8D2DC"/>
<path d="M15.3975 15.5405V18.239C15.3975 19.0615 14.7283 19.7303 13.9058 19.7303H7.73047V15.5405H15.3975Z" fill="#596C76"/>
<path d="M8.36774 0.802063V4.67286C8.36774 5.02355 8.08311 5.30817 7.73242 5.30817V0.166748C8.08311 0.166748 8.36774 0.451377 8.36774 0.802063Z" fill="#FF9F00"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,17 @@
<svg width="22" height="14" viewBox="0 0 22 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.6663 11.1257L7.02441 8.58667L10.833 13.6648H21.6663V11.1257Z" fill="#FFB54C"/>
<path d="M0 11.1257V13.6648H10.8333V8.58667L0 11.1257Z" fill="#FFCC4A"/>
<path d="M21.6663 11.1257V5.41284H20.0688L19.4341 6.04761L18.7993 5.41284H17.2018L16.5671 6.04761L15.9323 5.41284H14.3348L13.7 6.04761L13.0653 5.41284H11.4678L8.29395 8.26929L10.833 11.1257H21.6663Z" fill="#FFCC4A"/>
<path d="M10.8333 5.41284H10.1986H8.60107L7.96631 6.04761L7.33154 5.41284H5.73405L5.09928 6.04761L4.46452 5.41284H2.86702L2.23226 6.04761L1.59749 5.41284H0V11.1257H10.8333V5.41284Z" fill="#FFE278"/>
<path d="M21.0316 1.60449H10.833L10.1982 2.23926L10.833 2.87402H21.0316V1.60449Z" fill="#ABD5ED"/>
<path d="M10.8333 1.60449H1.26953V0.334961H0V4.14355H1.26953V2.87402H10.8333V1.60449Z" fill="#BFEBFF"/>
<path d="M20.3965 0.334961H21.666V4.14355H20.3965V0.334961Z" fill="#ABD5ED"/>
<path d="M1.59766 5.41284H2.86719V8.58667H1.59766V5.41284Z" fill="#587AA1"/>
<path d="M4.46484 5.41284H5.73438V7.31714H4.46484V5.41284Z" fill="#587AA1"/>
<path d="M7.33203 5.41284H8.60156V8.58667H7.33203V5.41284Z" fill="#587AA1"/>
<path d="M11.4678 7.31714H10.833L10.5156 6.99976V5.73022L10.833 5.41284H11.4678V7.31714Z" fill="#455F80"/>
<path d="M10.1982 5.41284H10.833V7.31714H10.1982V5.41284Z" fill="#587AA1"/>
<path d="M13.0654 5.41284H14.335V8.58667H13.0654V5.41284Z" fill="#455F80"/>
<path d="M15.9326 5.41284H17.2021V7.31714H15.9326V5.41284Z" fill="#455F80"/>
<path d="M18.7998 5.41284H20.0693V8.58667H18.7998V5.41284Z" fill="#455F80"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,21 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_6702_36698)">
<circle cx="20" cy="20" r="15" fill="#F4F4F4"/>
<path d="M26.4 13.1094H23.7333V12.582C23.7333 11.7097 23.0156 11 22.1333 11H17.8667C16.9844 11 16.2667 11.7097 16.2667 12.582V13.1094H13.6C12.7178 13.1094 12 13.8191 12 14.6914C12 15.392 12.4631 15.9873 13.1024 16.1947L14.0537 27.5493C14.1222 28.3628 14.8226 29 15.6481 29H24.3519C25.1775 29 25.8778 28.3628 25.9464 27.5491L26.8976 16.1947C27.5369 15.9873 28 15.392 28 14.6914C28 13.8191 27.2822 13.1094 26.4 13.1094ZM17.3333 12.582C17.3333 12.2913 17.5726 12.0547 17.8667 12.0547H22.1333C22.4274 12.0547 22.6667 12.2913 22.6667 12.582V13.1094H17.3333V12.582ZM24.8833 27.4618C24.8605 27.7329 24.6271 27.9453 24.3519 27.9453H15.6481C15.373 27.9453 15.1395 27.7329 15.1167 27.462L14.1793 16.2734H25.8207L24.8833 27.4618ZM26.4 15.2188H13.6C13.3059 15.2188 13.0667 14.9822 13.0667 14.6914C13.0667 14.4006 13.3059 14.1641 13.6 14.1641H26.4C26.6941 14.1641 26.9333 14.4006 26.9333 14.6914C26.9333 14.9822 26.6941 15.2188 26.4 15.2188Z" fill="#999999"/>
<path d="M17.8656 26.3307L17.3323 17.8229C17.314 17.5322 17.0596 17.3111 16.767 17.3292C16.473 17.3472 16.2494 17.5974 16.2676 17.8881L16.801 26.396C16.8185 26.6756 17.0533 26.8907 17.3328 26.8907C17.6416 26.8907 17.8846 26.6335 17.8656 26.3307Z" fill="#999999"/>
<path d="M20.0001 17.3281C19.7056 17.3281 19.4668 17.5642 19.4668 17.8555V26.3633C19.4668 26.6545 19.7056 26.8906 20.0001 26.8906C20.2947 26.8906 20.5335 26.6545 20.5335 26.3633V17.8555C20.5335 17.5642 20.2947 17.3281 20.0001 17.3281Z" fill="#999999"/>
<path d="M23.233 17.3292C22.9396 17.3111 22.6859 17.5321 22.6677 17.8229L22.1343 26.3307C22.1162 26.6213 22.3397 26.8716 22.6337 26.8896C22.9278 26.9076 23.1808 26.6865 23.199 26.3959L23.7323 17.8881C23.7505 17.5974 23.527 17.3472 23.233 17.3292Z" fill="#999999"/>
</g>
<defs>
<filter id="filter0_d_6702_36698" x="0" y="0" width="40" height="40" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6702_36698"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6702_36698" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,6 @@
<svg width="16" height="18" viewBox="0 0 16 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.4 2.10938H11.7333V1.58203C11.7333 0.709699 11.0156 0 10.1333 0H5.86667C4.98443 0 4.26667 0.709699 4.26667 1.58203V2.10938H1.6C0.71776 2.10938 0 2.81907 0 3.69141C0 4.392 0.463111 4.9873 1.1024 5.19472L2.05369 16.5493C2.1222 17.3628 2.82258 18 3.64814 18H12.3519C13.1775 18 13.8778 17.3628 13.9464 16.5491L14.8976 5.19469C15.5369 4.9873 16 4.392 16 3.69141C16 2.81907 15.2822 2.10938 14.4 2.10938ZM5.33333 1.58203C5.33333 1.29125 5.57259 1.05469 5.86667 1.05469H10.1333C10.4274 1.05469 10.6667 1.29125 10.6667 1.58203V2.10938H5.33333V1.58203ZM12.8833 16.4618C12.8605 16.7329 12.6271 16.9453 12.3519 16.9453H3.64814C3.37298 16.9453 3.13952 16.7329 3.11673 16.462L2.17934 5.27344H13.8207L12.8833 16.4618ZM14.4 4.21875H1.6C1.30592 4.21875 1.06667 3.98218 1.06667 3.69141C1.06667 3.40063 1.30592 3.16406 1.6 3.16406H14.4C14.6941 3.16406 14.9333 3.40063 14.9333 3.69141C14.9333 3.98218 14.6941 4.21875 14.4 4.21875Z" fill="#999999"/>
<path d="M5.86561 15.3307L5.33228 6.82286C5.31404 6.53215 5.05957 6.31106 4.76698 6.32916C4.47297 6.3472 4.24943 6.59744 4.26764 6.88811L4.80097 15.396C4.8185 15.6756 5.05331 15.8907 5.33278 15.8907C5.64165 15.8907 5.88456 15.6335 5.86561 15.3307Z" fill="#999999"/>
<path d="M7.99989 6.32812C7.70534 6.32812 7.46655 6.56423 7.46655 6.85547V15.3633C7.46655 15.6545 7.70534 15.8906 7.99989 15.8906C8.29443 15.8906 8.53322 15.6545 8.53322 15.3633V6.85547C8.53322 6.56423 8.29443 6.32812 7.99989 6.32812Z" fill="#999999"/>
<path d="M11.233 6.32915C10.9396 6.31112 10.6859 6.53215 10.6677 6.82285L10.1343 15.3307C10.1162 15.6213 10.3397 15.8716 10.6337 15.8896C10.9278 15.9076 11.1808 15.6865 11.199 15.3959L11.7323 6.8881C11.7505 6.5974 11.527 6.34715 11.233 6.32915Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,16 @@
<svg width="22" height="16" viewBox="0 0 22 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.9326 7.99996C12.9326 8.4282 12.8042 8.82647 12.5838 9.15837C12.2083 9.72412 11.5654 9.84106 10.8355 9.84106C9.67727 9.84106 9.05078 9.15816 9.05078 7.99996C9.05078 7.26923 9.11211 6.62577 9.67883 6.25036C10.0104 6.03074 10.408 5.90283 10.8354 5.90283C11.9937 5.90287 12.9326 6.84175 12.9326 7.99996Z" fill="#E5646E"/>
<path d="M11.4252 9.50734C10.267 9.50734 9.32807 8.56846 9.32807 7.41026C9.32807 6.98202 9.45843 6.5824 9.67877 6.25049C9.11302 6.62607 8.73828 7.27012 8.73828 8.00009C8.73828 9.15829 9.67716 10.0972 10.8354 10.0972C11.5653 10.0972 12.2082 9.72425 12.5837 9.15851C12.2519 9.3788 11.8534 9.50734 11.4252 9.50734Z" fill="#DB4655"/>
<path d="M3.66787 0.133789C3.84673 0.133789 4.02559 0.202058 4.16209 0.338597C4.16848 0.344987 4.1747 0.351463 4.18079 0.358023L3.79505 1.32708C2.01269 3.10948 1.03102 5.47931 1.03102 7.99997C1.03102 10.5206 2.01269 12.8905 3.79505 14.6729L4.18101 15.6417C4.17483 15.6483 4.16852 15.6549 4.16209 15.6613C3.88935 15.9344 3.44668 15.9344 3.1736 15.6613C1.12705 13.6149 0 10.8941 0 7.99997C0 5.10588 1.12705 2.38502 3.17364 0.338597C3.31001 0.202058 3.489 0.133789 3.66787 0.133789Z" fill="#FFE6E7"/>
<path d="M7.6231 4.08813C7.80196 4.08813 7.98099 4.1564 8.11732 4.29294C8.1312 4.30682 8.14437 4.32109 8.15681 4.33582L7.68282 5.28138C6.18391 6.78047 6.18391 9.21957 7.68282 10.7187L8.15774 11.6633C8.145 11.6783 8.13154 11.693 8.11732 11.7072C7.84458 11.9803 7.40157 11.9803 7.12884 11.7072C5.08466 9.66318 5.08466 6.33695 7.12884 4.29294C7.26521 4.15636 7.44424 4.08813 7.6231 4.08813Z" fill="#FF9A9F"/>
<path d="M5.64357 2.11108C5.82243 2.11108 6.0013 2.17935 6.13779 2.31589C6.14575 2.32385 6.15345 2.33189 6.16094 2.34014L5.56438 3.30433C2.97545 5.89361 2.97545 10.1066 5.56438 12.6959L6.16145 13.6596C6.15383 13.668 6.14596 13.6762 6.13783 13.6843C5.86476 13.9574 5.42243 13.9574 5.14935 13.6843C2.01532 10.5501 2.01532 5.45005 5.14935 2.31581C5.28585 2.17935 5.46471 2.11108 5.64357 2.11108Z" fill="#FFCCCF"/>
<path d="M6.29102 7.99972C6.29102 9.50473 7.0294 10.836 8.1565 11.6629C8.38886 11.3884 8.37511 10.9772 8.11608 10.7184C6.61717 9.21929 6.61717 6.78019 8.11608 5.28106C8.37511 5.0222 8.38793 4.61001 8.15557 4.33545C7.02852 5.1623 6.29102 6.49471 6.29102 7.99972Z" fill="#FF8086"/>
<path d="M3.49609 7.99983C3.49609 10.2771 4.53592 12.313 6.16362 13.6593C6.413 13.385 6.405 12.9604 6.14001 12.6956C3.55107 10.1064 3.55107 5.89335 6.14001 3.30408C6.405 3.03925 6.41249 2.61411 6.16316 2.33984C4.53541 3.68609 3.49609 5.72253 3.49609 7.99983Z" fill="#FF9A9F"/>
<path d="M0.699219 7.99986C0.699219 11.0495 2.05 13.7834 4.18124 15.6416C4.43535 15.3675 4.42896 14.9393 4.16232 14.6728C2.37992 12.8903 1.39825 10.5205 1.39825 7.99986C1.39825 5.47919 2.37992 3.10937 4.16232 1.32697C4.42896 1.06049 4.43514 0.632002 4.18103 0.35791C2.04979 2.21607 0.699219 4.95022 0.699219 7.99986Z" fill="#FFCCCF"/>
<path d="M18.0014 15.8662C17.8226 15.8662 17.6437 15.7979 17.5072 15.6613C17.5008 15.655 17.4946 15.6485 17.4885 15.6419L17.8742 14.6729C19.6566 12.8905 20.6383 10.5206 20.6383 7.99997C20.6383 5.4793 19.6566 3.10948 17.8742 1.32708L17.4883 0.358277C17.4945 0.351632 17.5008 0.345072 17.5072 0.338596C17.7799 0.06552 18.2226 0.06552 18.4957 0.338596C20.5422 2.38502 21.6693 5.10588 21.6693 7.99997C21.6693 10.8941 20.5422 13.6149 18.4956 15.6613C18.3593 15.7979 18.1803 15.8662 18.0014 15.8662Z" fill="#FFE6E7"/>
<path d="M14.0464 11.912C13.8675 11.912 13.6885 11.8438 13.5521 11.7072C13.5383 11.6933 13.5251 11.6791 13.5127 11.6644L13.9866 10.7187C15.4856 9.21966 15.4856 6.78055 13.9866 5.28143L13.5117 4.33687C13.5245 4.32181 13.5379 4.30716 13.5521 4.29294C13.8249 4.01987 14.2679 4.01987 14.5406 4.29294C16.5848 6.33695 16.5848 9.66318 14.5406 11.7072C14.4043 11.8438 14.2252 11.912 14.0464 11.912Z" fill="#FF9A9F"/>
<path d="M16.0257 13.8892C15.8468 13.8892 15.668 13.8209 15.5315 13.6844C15.5235 13.6764 15.5158 13.6684 15.5083 13.6601L16.1049 12.6959C18.6938 10.1066 18.6938 5.89361 16.1049 3.30433L15.5078 2.34065C15.5154 2.33223 15.5233 2.32398 15.5314 2.31589C15.8045 2.04281 16.2468 2.04281 16.5199 2.31589C19.6539 5.45009 19.6539 10.5502 16.5199 13.6844C16.3834 13.8209 16.2046 13.8892 16.0257 13.8892Z" fill="#FFCCCF"/>
<path d="M15.3797 8.00012C15.3797 6.49511 14.6413 5.16376 13.5142 4.33691C13.2819 4.61147 13.2956 5.02261 13.5546 5.28147C15.0536 6.78055 15.0536 9.21966 13.5546 10.7188C13.2956 10.9776 13.2828 11.3898 13.5152 11.6644C14.6422 10.8375 15.3797 9.50513 15.3797 8.00012Z" fill="#FF8086"/>
<path d="M18.175 8.00005C18.175 5.72275 17.1351 3.68687 15.5074 2.34058C15.2581 2.61484 15.2661 3.03944 15.531 3.30426C18.12 5.89353 18.12 10.1065 15.531 12.6958C15.2661 12.9606 15.2586 13.3858 15.5079 13.66C17.1356 12.3138 18.175 10.2774 18.175 8.00005Z" fill="#FF9A9F"/>
<path d="M20.9707 7.99989C20.9707 4.95025 19.6199 2.21636 17.4887 0.358154C17.2346 0.632246 17.241 1.06048 17.5076 1.32696C19.29 3.10936 20.2716 5.47918 20.2716 7.99985C20.2716 10.5205 19.29 12.8903 17.5076 14.6727C17.241 14.9392 17.2348 15.3677 17.4889 15.6418C19.6201 13.7837 20.9707 11.0495 20.9707 7.99989Z" fill="#FFCCCF"/>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@ -0,0 +1,21 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.91415 8.48211C6.17473 8.04425 4.3403 8.07332 2.60916 8.56611C1.74635 8.81143 0.920517 9.16778 0.154693 9.62511C0.00421161 9.71499 -0.0449192 9.90978 0.0449212 10.0603C0.130234 10.2033 0.336744 10.2556 0.480159 10.1701C1.19846 9.74106 1.97317 9.40684 2.78283 9.17663C4.40699 8.71422 6.12775 8.68689 7.75919 9.09766C7.92922 9.14032 8.10166 9.03732 8.14445 8.86737C8.18723 8.69742 8.0841 8.52489 7.91415 8.48211Z" fill="#6BBEF6"/>
<path d="M7.37225 10.4417C5.99155 10.1036 4.53693 10.1313 3.16562 10.5219C2.47009 10.7197 1.80528 11.0068 1.1896 11.3753C1.0392 11.4653 0.990237 11.6602 1.08025 11.8106C1.1397 11.91 1.24495 11.9651 1.3529 11.9651C1.40838 11.9651 1.46453 11.9505 1.51557 11.92C2.0836 11.58 2.69716 11.3151 3.33942 11.1324C4.60502 10.7719 5.94733 10.7463 7.22126 11.0583C7.39138 11.0999 7.56332 10.9958 7.60504 10.8255C7.64677 10.6553 7.54254 10.4835 7.37225 10.4417Z" fill="#6BBEF6"/>
<path d="M6.83178 12.4028C5.81116 12.1632 4.73634 12.1888 3.72363 12.4772C3.19665 12.627 2.6929 12.8452 2.22626 13.1255C2.07599 13.2158 2.02737 13.4108 2.11763 13.561C2.20697 13.7097 2.4048 13.7587 2.55312 13.6696C2.97186 13.4181 3.42406 13.2223 3.89735 13.0877C4.80646 12.8289 5.77096 12.8057 6.68663 13.0208C6.85739 13.0605 7.02809 12.9549 7.06817 12.7844C7.10824 12.6138 7.00241 12.4429 6.83178 12.4028Z" fill="#6BBEF6"/>
<path d="M14.4727 19.6416L13.6738 21.4527C13.5717 21.6842 13.3426 21.8336 13.0896 21.8336H8.12563C7.79678 21.8336 7.57643 21.4956 7.70914 21.1947L9.38433 17.397L13.3135 19.1303L14.4727 19.6416Z" fill="#FFDDCE"/>
<path d="M14.475 19.6415L13.6761 21.4526C13.574 21.6841 13.3448 21.8335 13.0918 21.8335H12.123L13.3158 19.1301L14.475 19.6415Z" fill="#FFCBBE"/>
<path d="M14.1855 13.4249L16.3003 14.3579L19.2365 7.70213C19.4938 7.119 19.229 6.43743 18.645 6.1798C18.061 5.92217 17.3791 6.18606 17.1218 6.7692L14.1855 13.4249Z" fill="#FFDDCE"/>
<path d="M18.6453 6.17967C18.4607 6.09821 18.2662 6.06906 18.0786 6.08543C18.326 6.40921 18.3944 6.85397 18.2185 7.25273L15.8676 12.5814C15.6524 13.0693 15.8731 13.6392 16.3607 13.8543L16.4962 13.9141L19.2368 7.70201C19.4941 7.11887 19.2292 6.43726 18.6453 6.17967Z" fill="#FFCBBE"/>
<path d="M12.0723 12.492L14.187 13.425L16.7716 7.56632C17.0291 6.98276 16.7647 6.30102 16.1812 6.04356L16.1797 6.04289C15.5961 5.78542 14.9144 6.04983 14.6569 6.63334L12.0723 12.492Z" fill="#FFDDCE"/>
<path d="M16.1808 6.04379L16.1789 6.04295C15.9946 5.96165 15.8006 5.93254 15.6132 5.949C15.8606 6.27286 15.9288 6.71808 15.7526 7.11744L13.757 11.6407C13.5398 12.1331 13.7626 12.7082 14.2546 12.9252L14.3821 12.9814L16.7708 7.56693C17.0284 6.98312 16.7642 6.30121 16.1808 6.04379Z" fill="#FFCBBE"/>
<path d="M8.56284 14.6994C7.94352 16.1032 8.58154 17.7448 9.98852 18.3655L13.7612 20.0298C14.9419 20.5507 16.3209 20.0175 16.8407 18.8391L21.1141 9.15261C21.2426 8.86126 21.2407 8.54557 21.1335 8.26982C21.0262 7.99412 20.8142 7.75968 20.5222 7.63091C19.9384 7.37337 19.2564 7.63705 18.9994 8.21972L16.4886 13.9111L12.2593 12.0453C12.2942 11.9675 14.112 7.84707 14.112 7.84707C14.2405 7.55571 14.2384 7.23939 14.1314 6.96428C14.0246 6.68875 13.8121 6.45414 13.5201 6.32537C12.9363 6.06782 12.2545 6.33104 11.9973 6.91418L8.56284 14.6994Z" fill="#FFDDCE"/>
<path d="M13.1203 6.51804C13.2273 6.79311 13.2294 7.10947 13.1008 7.40083C13.1008 7.40083 12.0882 9.6961 11.5601 10.8928C11.3879 11.2831 11.5646 11.7387 11.9547 11.9108L12.2666 12.0484C12.3015 11.9705 14.1192 7.85012 14.1192 7.85012C14.2478 7.55876 14.2456 7.24244 14.1387 6.96733C14.0319 6.6918 13.8194 6.45719 13.5275 6.32842C13.3429 6.247 13.1485 6.21776 12.9608 6.23409C13.0274 6.32105 13.0811 6.41673 13.1203 6.51804Z" fill="#FFCBBE"/>
<path d="M21.1416 8.27269C21.0344 7.99699 20.8223 7.76255 20.5303 7.63377C20.3457 7.55235 20.1513 7.52316 19.9636 7.53953C20.0301 7.62649 20.0837 7.72213 20.1231 7.8234C20.2304 8.0991 20.2322 8.41483 20.1037 8.70619L17.2515 15.1713C17.102 15.5102 16.7231 15.686 16.3689 15.5776C15.1754 15.2122 13.3924 15.1243 12.5647 17.0007C11.6818 19.0018 13.3307 19.8367 13.3307 19.8367L13.7693 20.0326C14.9499 20.5535 16.3289 20.0203 16.8488 18.842L21.1221 9.15548C21.2507 8.86412 21.2488 8.54839 21.1416 8.27269Z" fill="#FFCBBE"/>
<path d="M14.0754 16.7326C14.8301 16.2038 15.3009 16.203 16.2573 16.2547C16.5879 16.2725 17.1315 16.1379 17.3359 15.8777L19.3641 12.8099C19.8043 12.2496 20.616 12.1527 21.1758 12.5935C21.7348 13.0337 21.8308 13.8434 21.3901 14.402L18.8737 18.2526C18.8737 18.2526 17.9272 19.6558 16.9484 20.0884C15.5668 20.6991 13.919 20.3242 13.0262 19.5722C13.0262 19.5722 12.8878 19.3576 13.0925 18.2919C13.2237 17.6089 14.0754 16.7326 14.0754 16.7326Z" fill="#FFDDCE"/>
<path d="M21.1759 12.5933C20.874 12.3556 20.4991 12.2745 20.1486 12.3378C20.5354 12.7953 20.5607 13.4764 20.1751 13.9651L17.6587 17.8158C17.6587 17.8158 16.7122 19.2189 15.7334 19.6515C15.0065 19.9729 14.206 20.0211 13.4805 19.8853C14.4122 20.4147 15.7761 20.6063 16.9485 20.0881C17.9273 19.6555 18.8737 18.2523 18.8737 18.2523L21.3902 14.4017C21.8308 13.8432 21.7349 13.0335 21.1759 12.5933Z" fill="#FFCBBE"/>
<path d="M14.2333 4.09028H7.43247C6.34901 4.09028 5.4707 3.21197 5.4707 2.12851C5.4707 1.04505 6.34901 0.166748 7.43247 0.166748H14.2333C15.3167 0.166748 16.195 1.04505 16.195 2.12851C16.195 3.21197 15.3167 4.09028 14.2333 4.09028Z" fill="#5C90B9"/>
<path d="M14.2336 0.166748H12.9258C14.0092 0.166748 14.8875 1.04505 14.8875 2.12851C14.8875 3.21197 14.0092 4.09028 12.9258 4.09028H14.2336C15.3171 4.09028 16.1954 3.21197 16.1954 2.12851C16.1954 1.04505 15.3171 0.166748 14.2336 0.166748Z" fill="#4A80AA"/>
<path d="M10.8338 4.09028C11.92 4.09028 12.8004 3.21197 12.8004 2.12851C12.8004 1.04506 11.92 0.166748 10.8338 0.166748C9.74768 0.166748 8.86719 1.04506 8.86719 2.12851C8.86719 3.21197 9.74768 4.09028 10.8338 4.09028Z" fill="#FFD064"/>
<path d="M10.8329 4.09028C11.9163 4.09028 12.7946 3.21197 12.7946 2.12851C12.7946 1.04506 11.9163 0.166748 10.8329 0.166748C9.74941 0.166748 8.87109 1.04506 8.87109 2.12851C8.87109 3.21197 9.74941 4.09028 10.8329 4.09028Z" fill="#FFD064"/>
<path d="M10.834 2.78329C11.1954 2.78329 11.4884 2.49033 11.4884 2.12895C11.4884 1.76757 11.1954 1.47461 10.834 1.47461C10.4726 1.47461 10.1797 1.76757 10.1797 2.12895C10.1797 2.49033 10.4726 2.78329 10.834 2.78329Z" fill="#5C90B9"/>
</svg>

After

Width:  |  Height:  |  Size: 6.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.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,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.8334C7.93969 21.8334 5.21921 20.7065 3.17302 18.6604C1.12688 16.6143 0 13.8938 0 11.0001C0 8.10644 1.12688 5.38596 3.17298 3.33977C5.21921 1.29363 7.93961 0.166748 10.8334 0.166748C13.727 0.166748 16.4475 1.29363 18.4936 3.33977C20.5398 5.38596 21.6667 8.1064 21.6667 11.0001C21.6667 13.8939 20.5397 16.6143 18.4936 18.6605C16.4475 20.7067 13.727 21.8334 10.8334 21.8334ZM10.8334 3.12052C6.48853 3.12052 2.95378 6.65528 2.95378 11.0001C2.95378 15.345 6.48853 18.8797 10.8334 18.8797C15.1782 18.8797 18.713 15.3449 18.713 11.0001C18.713 6.65532 15.1781 3.12052 10.8334 3.12052Z" fill="#7DD2F0"/>
<g opacity="0.1">
<path d="M5.14215 18.6604C3.09605 16.6143 1.96917 13.8938 1.96917 11.0001C1.96917 8.1064 3.09605 5.38592 5.14215 3.33981C6.95445 1.52752 9.29585 0.437158 11.8179 0.211393C11.4924 0.182194 11.1641 0.166748 10.8334 0.166748C7.93969 0.166748 5.21921 1.29363 3.17302 3.33977C1.12688 5.38588 0 8.1064 0 11C0 13.8937 1.12688 16.6142 3.17298 18.6603C5.21913 20.7065 7.93961 21.8334 10.8333 21.8334C11.164 21.8334 11.4924 21.818 11.8179 21.7888C9.29585 21.563 6.95449 20.4726 5.14215 18.6604Z" fill="black"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

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.264C11.2545 10.264 11.6929 9.82553 11.6929 9.28469C11.6929 8.74385 11.2545 8.30542 10.7136 8.30542C10.1728 8.30542 9.73438 8.74385 9.73438 9.28469C9.73438 9.82553 10.1728 10.264 10.7136 10.264Z" fill="#62D8F9"/>
<path d="M10.7138 8.30566C10.6442 8.30566 10.5762 8.31307 10.5107 8.32686C10.9541 8.42039 11.2869 8.81373 11.2869 9.28494C11.2869 9.75614 10.9541 10.1495 10.5107 10.243C10.5763 10.2568 10.6442 10.2642 10.7138 10.2642C11.2546 10.2642 11.6931 9.82576 11.6931 9.28494C11.6931 8.74412 11.2547 8.30566 10.7138 8.30566Z" fill="#00BEF7"/>
<path d="M14.1217 13.3881L12.4086 13.1096L11.4778 11.3116C11.3944 11.1236 11.2634 10.8661 10.9741 10.78L9.88702 10.4538C9.76776 10.4247 9.65308 10.4141 9.4497 10.446L7.13145 10.9844C6.99264 11.0167 6.86865 11.0946 6.7794 11.2057L5.54163 12.7463C5.32157 13.0202 5.3652 13.4207 5.63913 13.6408C5.75661 13.7352 5.89736 13.7811 6.03718 13.7811C6.22338 13.7811 6.40789 13.6997 6.53357 13.5433L7.6336 12.1741L8.72165 11.9215L8.08273 14.0507C8.07045 14.0866 8.06652 14.1328 8.04896 14.234L7.68849 16.7928L6.00734 18.2515C5.74192 18.4818 5.71344 18.8836 5.94374 19.149C6.06959 19.294 6.24657 19.3683 6.4246 19.3683C6.57238 19.3683 6.72087 19.3171 6.84127 19.2126L8.70227 17.598C8.81826 17.4973 8.89393 17.3582 8.91534 17.2062L9.21009 15.114L9.24932 15.1257L10.8419 16.9844L11.7847 19.0313C11.892 19.2643 12.1223 19.4016 12.363 19.4016C12.452 19.4016 12.5425 19.3828 12.6287 19.343C12.9479 19.196 13.0874 18.8181 12.9405 18.499L11.9606 16.3716C11.936 16.3182 11.9042 16.2685 11.8659 16.2238L10.5325 14.6676L10.9911 13.1393L11.4257 13.9789C11.5183 14.1577 11.69 14.282 11.8886 14.3143L13.9174 14.6441C13.952 14.6497 13.9864 14.6524 14.0203 14.6524C14.3267 14.6524 14.5968 14.4304 14.6475 14.1182C14.7039 13.7713 14.4685 13.4445 14.1217 13.3881Z" fill="#62D8F9"/>
<path d="M8.99707 10.5511C9.20045 10.5192 9.31454 10.5321 9.4338 10.5611L10.5209 10.8873C10.8102 10.9735 10.9411 11.231 11.0245 11.419L12.0585 13.4273C12.085 13.4787 12.1345 13.5144 12.1917 13.5232L14.645 13.9017C14.5979 13.6445 14.3947 13.4324 14.1218 13.3881L12.4088 13.1096L11.4779 11.3116C11.3945 11.1236 11.2635 10.8661 10.9742 10.78L9.88716 10.4538C9.7679 10.4247 9.65322 10.4141 9.44984 10.446L8.99707 10.5511Z" fill="#00BEF7"/>
<path d="M11.9615 16.3715C11.9369 16.3181 11.905 16.2684 11.8667 16.2237L10.5333 14.6676L10.9919 13.1396L10.9337 13.0271C10.8579 12.8807 10.6424 12.9008 10.595 13.0587L10.1416 14.5696C10.1032 14.6976 10.1325 14.8363 10.2194 14.9377L11.4134 16.3311C11.4516 16.3758 11.4835 16.4256 11.5081 16.4789L12.4879 18.6063C12.6191 18.8912 12.5216 19.2223 12.2723 19.3948C12.3025 19.3992 12.3331 19.4015 12.3638 19.4015C12.4528 19.4015 12.5433 19.3827 12.6295 19.343C12.9487 19.196 13.0882 18.8181 12.9413 18.499L11.9615 16.3715Z" fill="#00BEF7"/>
<path d="M3.47827 18.4406C3.31064 18.4406 3.17032 18.3095 3.16092 18.1401C3.08839 16.833 4.09282 15.7105 5.39991 15.6379C5.57574 15.6288 5.72551 15.7624 5.7352 15.9379C5.74493 16.1133 5.61065 16.2634 5.4352 16.2732C4.47834 16.3263 3.74305 17.148 3.79616 18.1048C3.8059 18.2802 3.67162 18.4304 3.49617 18.4401C3.4902 18.4404 3.48419 18.4406 3.47827 18.4406Z" fill="#00BEF7"/>
<path d="M4.76838 18.4539C4.60076 18.4539 4.46044 18.3228 4.45104 18.1534C4.41524 17.5085 4.91082 16.9547 5.5557 16.9189C5.73081 16.9093 5.88121 17.0435 5.89094 17.219C5.90068 17.3944 5.76636 17.5445 5.59091 17.5542C5.29634 17.5706 5.06994 17.8236 5.08627 18.1182C5.09601 18.2936 4.96169 18.4437 4.78624 18.4535C4.78032 18.4537 4.77431 18.4539 4.76838 18.4539Z" fill="#00BEF7"/>
<path d="M17.0106 13.6226C17.0076 13.6226 17.0046 13.6226 17.0016 13.6225C16.826 13.6176 16.6876 13.4712 16.6925 13.2956C16.7191 12.3377 15.9615 11.5366 15.0036 11.5099C14.828 11.505 14.6896 11.3587 14.6945 11.183C14.6994 11.0074 14.8466 10.8691 15.0213 10.8739C16.3299 10.9103 17.365 12.0047 17.3285 13.3134C17.3237 13.4859 17.1822 13.6226 17.0106 13.6226Z" fill="#00BEF7"/>
<path d="M15.7216 13.6714C15.7186 13.6714 15.7156 13.6713 15.7126 13.6712C15.537 13.6663 15.3986 13.52 15.4035 13.3444C15.4075 13.2015 15.3555 13.0656 15.2574 12.9618C15.1591 12.8579 15.0263 12.7985 14.8835 12.7946C14.7079 12.7897 14.5695 12.6433 14.5743 12.4677C14.5793 12.2921 14.724 12.1534 14.9012 12.1586C15.214 12.1673 15.5046 12.2973 15.7196 12.5246C15.9346 12.752 16.0482 13.0494 16.0395 13.3621C16.0346 13.5347 15.8932 13.6714 15.7216 13.6714Z" fill="#00BEF7"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,29 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.18147 12.617H0.352631C0.157897 12.617 0 12.4719 0 12.2928C0 12.1137 0.157897 11.9685 0.352631 11.9685H5.18147C5.37624 11.9685 5.5341 12.1137 5.5341 12.2928C5.5341 12.4719 5.37624 12.617 5.18147 12.617Z" fill="#C5D3DD"/>
<path d="M5.37006 14.6529H1.70029C1.50555 14.6529 1.34766 14.5077 1.34766 14.3287C1.34766 14.1496 1.50555 14.0044 1.70029 14.0044H5.3701C5.56483 14.0044 5.72273 14.1496 5.72273 14.3287C5.72269 14.5077 5.56483 14.6529 5.37006 14.6529Z" fill="#C5D3DD"/>
<path d="M6.10302 16.3546H3.87607C3.68133 16.3546 3.52344 16.2094 3.52344 16.0303C3.52344 15.8512 3.68133 15.7061 3.87607 15.7061H6.10302C6.29775 15.7061 6.45565 15.8512 6.45565 16.0303C6.45565 16.2094 6.29779 16.3546 6.10302 16.3546Z" fill="#C5D3DD"/>
<path d="M5.70018 10.5809H2.03037C1.83563 10.5809 1.67773 10.4357 1.67773 10.2566C1.67773 10.0776 1.83563 9.93237 2.03037 9.93237H5.70018C5.89495 9.93237 6.05281 10.0776 6.05281 10.2566C6.05281 10.4358 5.89491 10.5809 5.70018 10.5809Z" fill="#C5D3DD"/>
<path d="M6.43505 8.87901H4.2081C4.01332 8.87901 3.85547 8.73382 3.85547 8.55474C3.85547 8.37565 4.01337 8.23047 4.2081 8.23047H6.43505C6.62978 8.23047 6.78768 8.37565 6.78768 8.55474C6.78768 8.73382 6.62978 8.87901 6.43505 8.87901Z" fill="#C5D3DD"/>
<path d="M9.54009 6.56555L7.89129 4.64779C7.77455 4.51203 7.56983 4.4965 7.43398 4.61333L6.40778 5.4956C6.34258 5.55167 6.30233 5.63136 6.29584 5.71709C6.28936 5.80283 6.31725 5.88766 6.37332 5.9529L8.02212 7.87067C8.08624 7.94525 8.17695 7.98351 8.26813 7.98351C8.34297 7.98351 8.4182 7.95775 8.47938 7.90513L9.50559 7.02286C9.57078 6.96678 9.61104 6.8871 9.61752 6.80136C9.62405 6.71558 9.59616 6.63075 9.54009 6.56555Z" fill="#FEDF30"/>
<path d="M8.57688 4.05987L7.88066 3.25006C7.76388 3.1143 7.5592 3.09882 7.42335 3.2156L5.02887 5.27423C4.96367 5.33031 4.92341 5.40999 4.91693 5.49573C4.91049 5.58147 4.93833 5.6663 4.99441 5.73154L5.69063 6.54135C5.75475 6.61593 5.84542 6.65419 5.93664 6.65419C6.01149 6.65419 6.08672 6.62842 6.1479 6.5758L8.54238 4.51718C8.60758 4.4611 8.64783 4.38142 8.65432 4.29568C8.6608 4.2099 8.63292 4.12507 8.57688 4.05987Z" fill="#D7AA02"/>
<path d="M20.2699 5.71709C20.2634 5.63135 20.2231 5.55167 20.1579 5.49559L19.1317 4.61332C18.9959 4.49654 18.7912 4.51198 18.6744 4.64778L17.0256 6.56555C16.9696 6.63075 16.9417 6.71558 16.9482 6.80136C16.9547 6.8871 16.9949 6.96678 17.0601 7.02286L18.0863 7.90513C18.1476 7.95774 18.2227 7.98351 18.2976 7.98351C18.3888 7.98351 18.4795 7.94525 18.5436 7.87067L20.1924 5.9529C20.2485 5.88766 20.2763 5.80287 20.2699 5.71709Z" fill="#FEDF30"/>
<path d="M21.5385 5.27382L19.144 3.21519C19.0788 3.15912 18.994 3.13093 18.9082 3.13771C18.8225 3.14416 18.7428 3.18445 18.6867 3.24965L17.9905 4.05946C17.8737 4.19526 17.8892 4.39998 18.025 4.51672L20.4195 6.57535C20.4785 6.62611 20.5536 6.65374 20.6309 6.65374C20.6391 6.65374 20.6472 6.65343 20.6553 6.65283C20.7411 6.64634 20.8207 6.60609 20.8768 6.54085L21.573 5.73104C21.6898 5.59532 21.6743 5.39056 21.5385 5.27382Z" fill="#D7AA02"/>
<path d="M12.3359 2.71826H14.3258V5.56015H12.3359V2.71826Z" fill="#0055A3"/>
<path d="M14.5486 0.899902H10.8925C10.5495 0.899902 10.2715 1.17795 10.2715 1.52094V2.27541C10.2715 2.6184 10.5495 2.89645 10.8925 2.89645H14.529L15.412 2.65843V1.153L14.5486 0.899902Z" fill="#0473CE"/>
<path d="M15.7658 0.899902H14.4688C14.8117 0.899902 15.0898 1.17795 15.0898 1.52094V2.27541C15.0898 2.6184 14.8117 2.89645 14.4688 2.89645H15.7658C16.1088 2.89645 16.3869 2.6184 16.3869 2.27541V1.52094C16.3869 1.17795 16.1088 0.899902 15.7658 0.899902Z" fill="#0055A3"/>
<path d="M11.1894 20.8213C6.73857 19.6401 4.08809 15.0744 5.26933 10.6236C6.22178 7.03487 9.37457 4.61661 12.8933 4.43364C13.7386 4.38971 21.1366 5.74662 21.1366 12.5075C21.1366 17.4616 16.6175 21.3883 12.6796 21.0765C12.1846 21.0373 11.6863 20.9532 11.1894 20.8213Z" fill="#26A6FE"/>
<path d="M13.3282 4.42456C13.11 4.42456 12.8937 4.43303 12.6797 4.44951C16.9815 4.78048 20.369 8.37579 20.369 12.7625C20.369 17.1491 16.9814 20.7444 12.6797 21.0754C12.8937 21.0919 13.11 21.1003 13.3282 21.1003C17.9331 21.1003 21.6661 17.3674 21.6661 12.7625C21.6661 8.15754 17.9331 4.42456 13.3282 4.42456Z" fill="#0593FC"/>
<path d="M13.3291 19.2553C16.9152 19.2553 19.8222 16.3483 19.8222 12.7622C19.8222 9.17613 16.9152 6.26904 13.3291 6.26904C9.74302 6.26904 6.83594 9.17613 6.83594 12.7622C6.83594 16.3483 9.74302 19.2553 13.3291 19.2553Z" fill="#FFF3EF"/>
<path d="M14.167 6.32385C13.6602 6.25778 13.1594 6.25229 12.6719 6.30141C12.7378 6.30798 12.8038 6.31524 12.8699 6.32385C16.426 6.78721 18.933 10.0456 18.4696 13.6016C18.0723 16.6508 15.6199 18.9285 12.687 19.2238C16.1652 19.5707 19.3119 17.0915 19.7667 13.6016C20.2301 10.0456 17.723 6.78721 14.167 6.32385Z" fill="#F1F0F0"/>
<path d="M13.3291 8.12532C13.1625 8.12532 13.0273 7.99021 13.0273 7.82353V6.98977C13.0273 6.8231 13.1625 6.68799 13.3291 6.68799C13.4958 6.68799 13.6309 6.8231 13.6309 6.98977V7.82353C13.6309 7.99021 13.4958 8.12532 13.3291 8.12532Z" fill="#B6C4CF"/>
<path d="M16.8487 9.56667C16.7714 9.56667 16.6942 9.53722 16.6353 9.47829C16.5174 9.36043 16.5174 9.16937 16.6353 9.05151L17.2249 8.46195C17.3427 8.34404 17.5337 8.34409 17.6517 8.46195C17.7695 8.57981 17.7695 8.77087 17.6516 8.88873L17.062 9.47829C17.0031 9.53718 16.9259 9.56667 16.8487 9.56667Z" fill="#B6C4CF"/>
<path d="M19.1512 13.0738H18.3174C18.1507 13.0738 18.0156 12.9387 18.0156 12.772C18.0156 12.6053 18.1507 12.4702 18.3174 12.4702H19.1512C19.3178 12.4702 19.453 12.6053 19.453 12.772C19.453 12.9387 19.3178 13.0738 19.1512 13.0738Z" fill="#B6C4CF"/>
<path d="M17.4656 17.1814C17.3884 17.1814 17.3111 17.1519 17.2522 17.093L16.6626 16.5035C16.5448 16.3856 16.5448 16.1945 16.6626 16.0767C16.7805 15.9588 16.9715 15.9588 17.0894 16.0767L17.679 16.6662C17.7969 16.7841 17.7969 16.9752 17.679 17.093C17.6201 17.152 17.5428 17.1814 17.4656 17.1814Z" fill="#B6C4CF"/>
<path d="M13.3682 18.8934C13.2015 18.8934 13.0664 18.7583 13.0664 18.5916V17.7578C13.0664 17.5912 13.2015 17.4561 13.3682 17.4561C13.5349 17.4561 13.67 17.5912 13.67 17.7578V18.5916C13.67 18.7583 13.5349 18.8934 13.3682 18.8934Z" fill="#B6C4CF"/>
<path d="M9.26075 17.2073C9.18353 17.2073 9.10627 17.1779 9.04738 17.1189C8.92952 17.001 8.92952 16.81 9.04738 16.6921L9.63694 16.1026C9.7548 15.9847 9.94586 15.9847 10.0637 16.1026C10.1816 16.2204 10.1816 16.4115 10.0637 16.5293L9.47416 17.1189C9.41523 17.1778 9.33797 17.2073 9.26075 17.2073Z" fill="#B6C4CF"/>
<path d="M8.38164 13.1114H7.54788C7.38121 13.1114 7.24609 12.9763 7.24609 12.8096C7.24609 12.6429 7.38121 12.5078 7.54788 12.5078H8.38164C8.54831 12.5078 8.68342 12.6429 8.68342 12.8096C8.68342 12.9763 8.54831 13.1114 8.38164 13.1114Z" fill="#B6C4CF"/>
<path d="M9.82297 9.59328C9.74575 9.59328 9.66849 9.56383 9.6096 9.5049L9.02004 8.91534C8.90218 8.79748 8.90218 8.60642 9.02004 8.48856C9.1379 8.3707 9.32896 8.37065 9.44682 8.48856L10.0364 9.07812C10.1542 9.19598 10.1542 9.38704 10.0364 9.5049C9.97745 9.56383 9.90019 9.59328 9.82297 9.59328Z" fill="#B6C4CF"/>
<path d="M13.3282 12.6168C13.149 12.6168 13.0039 12.4717 13.0039 12.2926V10.1121C13.0039 9.93303 13.149 9.78784 13.3282 9.78784C13.5073 9.78784 13.6524 9.93303 13.6524 10.1121V12.2926C13.6524 12.4717 13.5073 12.6168 13.3282 12.6168Z" fill="#678D98"/>
<path d="M16.0561 15.7395C15.9731 15.7395 15.8901 15.7078 15.8268 15.6445L13.4661 13.2838C13.3394 13.1571 13.3394 12.9518 13.4661 12.8252C13.5928 12.6986 13.7981 12.6986 13.9247 12.8252L16.2854 15.1859C16.412 15.3126 16.412 15.5179 16.2854 15.6445C16.2221 15.7078 16.1391 15.7395 16.0561 15.7395Z" fill="#678D98"/>
<path d="M13.3724 13.3816C13.0131 13.3816 12.7207 13.0893 12.7207 12.73C12.7207 12.3707 13.013 12.0784 13.3724 12.0784C13.7316 12.0784 14.024 12.3707 14.024 12.73C14.024 13.0893 13.7316 13.3816 13.3724 13.3816Z" fill="#3E5959"/>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,25 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_6702_36714)">
<circle cx="20" cy="20" r="15" fill="#F4F4F4"/>
<path d="M21.1979 12.3395L18.26 15.2772C17.7228 15.8146 17.3477 16.4536 17.1345 17.1328C16.437 17.3525 15.798 17.7393 15.2772 18.26L12.3395 21.1979C10.5536 22.9837 10.5534 25.8744 12.3395 27.6605C14.1253 29.4464 17.0161 29.4466 18.8022 27.6605L21.7399 24.7227C22.2773 24.1855 22.6522 23.5465 22.8655 22.8671C23.5631 22.6475 24.202 22.2607 24.7227 21.7399L27.6605 18.8022C29.4464 17.0162 29.4467 14.1256 27.6605 12.3395C25.8746 10.5536 22.984 10.5534 21.1979 12.3395ZM17.266 20.2484C17.4886 20.7914 17.8199 21.2998 18.26 21.7399C18.6877 22.1674 19.1965 22.5054 19.7513 22.7343L16.8137 25.6721C16.1284 26.3572 15.0133 26.3574 14.328 25.6721C13.6427 24.9867 13.6427 23.8717 14.328 23.1864L17.2657 20.2485C17.2659 20.2485 17.2659 20.2485 17.266 20.2484ZM21.2428 24.2256L18.3049 27.1633C16.7939 28.6745 14.3479 28.6747 12.8366 27.1633C11.3254 25.6523 11.3253 23.2062 12.8366 21.695L15.7744 18.7571C16.1166 18.4149 16.5191 18.1412 16.9579 17.9488C16.8924 18.4845 16.9223 19.014 17.0364 19.5199C16.9419 19.5905 16.8524 19.6676 16.7686 19.7514L13.8309 22.6892C12.8715 23.6487 12.8715 25.2097 13.8309 26.1691C14.7903 27.1285 16.3513 27.1285 17.3108 26.1691L20.2485 23.2313C21.2089 22.2709 21.209 20.7119 20.2485 19.7514C19.7355 19.2383 19.6105 18.4924 19.8554 17.8663C20.3734 18.059 20.8488 18.3631 21.2428 18.7571C22.7504 20.2647 22.7505 22.7179 21.2428 24.2256ZM27.1633 18.3049L24.2256 21.2428C23.8834 21.585 23.4809 21.8587 23.0421 22.0511C23.1076 21.5154 23.0777 20.986 22.9637 20.4801C23.058 20.4095 23.1477 20.3323 23.2313 20.2485L26.1692 17.3108C27.1286 16.3514 27.1286 14.7903 26.1692 13.8309C25.3077 12.9695 23.9604 12.8807 22.9987 13.5684C22.8408 13.6813 22.8044 13.9009 22.9173 14.0588C23.0301 14.2168 23.2497 14.2533 23.4077 14.1403C24.1093 13.6386 25.0615 13.7174 25.6721 14.328C26.3574 15.0133 26.3574 16.1283 25.6721 16.8137L22.7342 19.7514C22.7342 19.7514 22.7342 19.7514 22.7341 19.7515C22.5113 19.2085 22.1801 18.7002 21.7399 18.26C21.3124 17.8325 20.8035 17.4945 20.2487 17.2656L21.4465 16.0678C21.5837 15.9306 21.5837 15.708 21.4465 15.5707C21.3091 15.4335 21.0867 15.4335 20.9493 15.5707L19.7514 16.7686C18.7911 17.729 18.7909 19.2879 19.7514 20.2485C20.2645 20.7615 20.3894 21.5076 20.1446 22.1337C19.6266 21.941 19.1511 21.6368 18.7571 21.2427C17.2497 19.7352 17.2495 17.282 18.7571 15.7744L21.695 12.8366C23.2061 11.3254 25.6522 11.3252 27.1633 12.8366C28.6745 14.3476 28.6747 16.7937 27.1633 18.3049Z" fill="#999999"/>
<path d="M22.5443 14.8262C22.5443 15.0204 22.3869 15.1777 22.1929 15.1777C21.9987 15.1777 21.8413 15.0204 21.8413 14.8262C21.8413 14.632 21.9987 14.4746 22.1929 14.4746C22.3869 14.4746 22.5443 14.632 22.5443 14.8262Z" fill="#999999"/>
<path d="M15.7755 15.774C15.9128 15.6368 15.9128 15.4142 15.7755 15.2769L14.2841 13.7855C14.1468 13.6483 13.9243 13.6483 13.787 13.7855C13.6498 13.9228 13.6498 14.1455 13.787 14.2828L15.2784 15.7742C15.4158 15.9114 15.6383 15.9114 15.7755 15.774Z" fill="#999999"/>
<path d="M12.3378 16.9062C12.1437 16.9062 11.9863 17.0636 11.9863 17.2577C11.9863 17.4519 12.1437 17.6092 12.3378 17.6092H14.447C14.641 17.6092 14.7984 17.4519 14.7984 17.2577C14.7984 17.0636 14.641 16.9062 14.447 16.9062H12.3378Z" fill="#999999"/>
<path d="M16.9062 12.2314V14.3405C16.9062 14.5346 17.0636 14.6921 17.2577 14.6921C17.4519 14.6921 17.6092 14.5346 17.6092 14.3405V12.2314C17.6092 12.0373 17.4519 11.8799 17.2577 11.8799C17.0636 11.8799 16.9062 12.0373 16.9062 12.2314Z" fill="#999999"/>
<path d="M24.227 24.2259C24.0897 24.3631 24.0897 24.5857 24.227 24.7231L25.7184 26.2145C25.7871 26.2831 25.877 26.3175 25.967 26.3175C26.2772 26.3175 26.4377 25.9397 26.2155 25.7173L24.7242 24.2259C24.5868 24.0887 24.3643 24.0887 24.227 24.2259Z" fill="#999999"/>
<path d="M23.0605 27.7673V25.6581C23.0605 25.464 22.903 25.3066 22.709 25.3066C22.5148 25.3066 22.3574 25.464 22.3574 25.6581V27.7673C22.3574 27.9614 22.5148 28.1187 22.709 28.1187C22.903 28.1187 23.0605 27.9614 23.0605 27.7673Z" fill="#999999"/>
<path d="M27.7693 23.0586C27.9633 23.0586 28.1207 22.9011 28.1207 22.707C28.1207 22.5128 27.9633 22.3555 27.7693 22.3555H25.66C25.466 22.3555 25.3086 22.5128 25.3086 22.707C25.3086 22.9011 25.466 23.0586 25.66 23.0586H27.7693Z" fill="#999999"/>
</g>
<defs>
<filter id="filter0_d_6702_36714" x="0" y="0" width="40" height="40" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6702_36714"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6702_36714" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,4 @@
<svg width="15" height="14" viewBox="0 0 15 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.13597 13.8173C7.21711 13.9314 7.35373 14 7.5 14C7.64627 14 7.78289 13.9315 7.86403 13.8173C8.89942 12.3614 10.4245 10.5632 11.4872 8.73444C12.3369 7.27221 12.75 6.02509 12.75 4.92188C12.75 2.20795 10.3948 0 7.5 0C4.60515 0 2.25 2.20795 2.25 4.92188C2.25 6.02509 2.66309 7.27221 3.51283 8.73444C4.57476 10.5618 6.10271 12.3644 7.13597 13.8173ZM7.5 0.820312C9.91237 0.820312 11.875 2.66027 11.875 4.92188C11.875 5.88448 11.4968 7.00323 10.7188 8.342C9.80277 9.91834 8.49557 11.5174 7.5 12.8617C6.50457 11.5176 5.19729 9.91843 4.2812 8.342C3.5032 7.00323 3.125 5.88448 3.125 4.92188C3.125 2.66027 5.08763 0.820312 7.5 0.820312Z" fill="#999999"/>
<path d="M7.5 7.38281C8.94742 7.38281 10.125 6.27884 10.125 4.92188C10.125 3.56491 8.94742 2.46094 7.5 2.46094C6.05257 2.46094 4.875 3.56491 4.875 4.92188C4.875 6.27884 6.05257 7.38281 7.5 7.38281ZM7.5 3.28125C8.46495 3.28125 9.25 4.01723 9.25 4.92188C9.25 5.82652 8.46495 6.5625 7.5 6.5625C6.53505 6.5625 5.75 5.82652 5.75 4.92188C5.75 4.01723 6.53505 3.28125 7.5 3.28125Z" fill="#999999"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M43.2614 20.4808C44.1769 21.3963 44.1769 22.8804 43.2614 23.7955L27.5381 39.5192C26.6226 40.4343 25.139 40.4343 24.2235 39.5192L16.7386 32.0338C15.8231 31.1188 15.8231 29.6347 16.7386 28.7196C17.6537 27.8041 19.1377 27.8041 20.0528 28.7196L25.8806 34.5474L39.9467 20.4808C40.8623 19.5657 42.3463 19.5657 43.2614 20.4808ZM60 30C60 46.5825 46.5802 60 30 60C13.4175 60 0 46.5802 0 30C0 13.4175 13.4198 0 30 0C46.5825 0 60 13.4198 60 30ZM55.3125 30C55.3125 16.0085 43.9897 4.6875 30 4.6875C16.0085 4.6875 4.6875 16.0103 4.6875 30C4.6875 43.9915 16.0103 55.3125 30 55.3125C43.9915 55.3125 55.3125 43.9897 55.3125 30Z" fill="#023DFE" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 761 B

View File

@ -0,0 +1,21 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_6702_36698)">
<circle cx="20" cy="20" r="15" fill="#F4F4F4"/>
<path d="M26.4 13.1094H23.7333V12.582C23.7333 11.7097 23.0156 11 22.1333 11H17.8667C16.9844 11 16.2667 11.7097 16.2667 12.582V13.1094H13.6C12.7178 13.1094 12 13.8191 12 14.6914C12 15.392 12.4631 15.9873 13.1024 16.1947L14.0537 27.5493C14.1222 28.3628 14.8226 29 15.6481 29H24.3519C25.1775 29 25.8778 28.3628 25.9464 27.5491L26.8976 16.1947C27.5369 15.9873 28 15.392 28 14.6914C28 13.8191 27.2822 13.1094 26.4 13.1094ZM17.3333 12.582C17.3333 12.2913 17.5726 12.0547 17.8667 12.0547H22.1333C22.4274 12.0547 22.6667 12.2913 22.6667 12.582V13.1094H17.3333V12.582ZM24.8833 27.4618C24.8605 27.7329 24.6271 27.9453 24.3519 27.9453H15.6481C15.373 27.9453 15.1395 27.7329 15.1167 27.462L14.1793 16.2734H25.8207L24.8833 27.4618ZM26.4 15.2188H13.6C13.3059 15.2188 13.0667 14.9822 13.0667 14.6914C13.0667 14.4006 13.3059 14.1641 13.6 14.1641H26.4C26.6941 14.1641 26.9333 14.4006 26.9333 14.6914C26.9333 14.9822 26.6941 15.2188 26.4 15.2188Z" fill="#999999"/>
<path d="M17.8656 26.3307L17.3323 17.8229C17.314 17.5322 17.0596 17.3111 16.767 17.3292C16.473 17.3472 16.2494 17.5974 16.2676 17.8881L16.801 26.396C16.8185 26.6756 17.0533 26.8907 17.3328 26.8907C17.6416 26.8907 17.8846 26.6335 17.8656 26.3307Z" fill="#999999"/>
<path d="M20.0001 17.3281C19.7056 17.3281 19.4668 17.5642 19.4668 17.8555V26.3633C19.4668 26.6545 19.7056 26.8906 20.0001 26.8906C20.2947 26.8906 20.5335 26.6545 20.5335 26.3633V17.8555C20.5335 17.5642 20.2947 17.3281 20.0001 17.3281Z" fill="#999999"/>
<path d="M23.233 17.3292C22.9396 17.3111 22.6859 17.5321 22.6677 17.8229L22.1343 26.3307C22.1162 26.6213 22.3397 26.8716 22.6337 26.8896C22.9278 26.9076 23.1808 26.6865 23.199 26.3959L23.7323 17.8881C23.7505 17.5974 23.527 17.3472 23.233 17.3292Z" fill="#999999"/>
</g>
<defs>
<filter id="filter0_d_6702_36698" x="0" y="0" width="40" height="40" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6702_36698"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6702_36698" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,25 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_6702_36714)">
<circle cx="20" cy="20" r="15" fill="#F4F4F4"/>
<path d="M21.1979 12.3395L18.26 15.2772C17.7228 15.8146 17.3477 16.4536 17.1345 17.1328C16.437 17.3525 15.798 17.7393 15.2772 18.26L12.3395 21.1979C10.5536 22.9837 10.5534 25.8744 12.3395 27.6605C14.1253 29.4464 17.0161 29.4466 18.8022 27.6605L21.7399 24.7227C22.2773 24.1855 22.6522 23.5465 22.8655 22.8671C23.5631 22.6475 24.202 22.2607 24.7227 21.7399L27.6605 18.8022C29.4464 17.0162 29.4467 14.1256 27.6605 12.3395C25.8746 10.5536 22.984 10.5534 21.1979 12.3395ZM17.266 20.2484C17.4886 20.7914 17.8199 21.2998 18.26 21.7399C18.6877 22.1674 19.1965 22.5054 19.7513 22.7343L16.8137 25.6721C16.1284 26.3572 15.0133 26.3574 14.328 25.6721C13.6427 24.9867 13.6427 23.8717 14.328 23.1864L17.2657 20.2485C17.2659 20.2485 17.2659 20.2485 17.266 20.2484ZM21.2428 24.2256L18.3049 27.1633C16.7939 28.6745 14.3479 28.6747 12.8366 27.1633C11.3254 25.6523 11.3253 23.2062 12.8366 21.695L15.7744 18.7571C16.1166 18.4149 16.5191 18.1412 16.9579 17.9488C16.8924 18.4845 16.9223 19.014 17.0364 19.5199C16.9419 19.5905 16.8524 19.6676 16.7686 19.7514L13.8309 22.6892C12.8715 23.6487 12.8715 25.2097 13.8309 26.1691C14.7903 27.1285 16.3513 27.1285 17.3108 26.1691L20.2485 23.2313C21.2089 22.2709 21.209 20.7119 20.2485 19.7514C19.7355 19.2383 19.6105 18.4924 19.8554 17.8663C20.3734 18.059 20.8488 18.3631 21.2428 18.7571C22.7504 20.2647 22.7505 22.7179 21.2428 24.2256ZM27.1633 18.3049L24.2256 21.2428C23.8834 21.585 23.4809 21.8587 23.0421 22.0511C23.1076 21.5154 23.0777 20.986 22.9637 20.4801C23.058 20.4095 23.1477 20.3323 23.2313 20.2485L26.1692 17.3108C27.1286 16.3514 27.1286 14.7903 26.1692 13.8309C25.3077 12.9695 23.9604 12.8807 22.9987 13.5684C22.8408 13.6813 22.8044 13.9009 22.9173 14.0588C23.0301 14.2168 23.2497 14.2533 23.4077 14.1403C24.1093 13.6386 25.0615 13.7174 25.6721 14.328C26.3574 15.0133 26.3574 16.1283 25.6721 16.8137L22.7342 19.7514C22.7342 19.7514 22.7342 19.7514 22.7341 19.7515C22.5113 19.2085 22.1801 18.7002 21.7399 18.26C21.3124 17.8325 20.8035 17.4945 20.2487 17.2656L21.4465 16.0678C21.5837 15.9306 21.5837 15.708 21.4465 15.5707C21.3091 15.4335 21.0867 15.4335 20.9493 15.5707L19.7514 16.7686C18.7911 17.729 18.7909 19.2879 19.7514 20.2485C20.2645 20.7615 20.3894 21.5076 20.1446 22.1337C19.6266 21.941 19.1511 21.6368 18.7571 21.2427C17.2497 19.7352 17.2495 17.282 18.7571 15.7744L21.695 12.8366C23.2061 11.3254 25.6522 11.3252 27.1633 12.8366C28.6745 14.3476 28.6747 16.7937 27.1633 18.3049Z" fill="#999999"/>
<path d="M22.5443 14.8262C22.5443 15.0204 22.3869 15.1777 22.1929 15.1777C21.9987 15.1777 21.8413 15.0204 21.8413 14.8262C21.8413 14.632 21.9987 14.4746 22.1929 14.4746C22.3869 14.4746 22.5443 14.632 22.5443 14.8262Z" fill="#999999"/>
<path d="M15.7755 15.774C15.9128 15.6368 15.9128 15.4142 15.7755 15.2769L14.2841 13.7855C14.1468 13.6483 13.9243 13.6483 13.787 13.7855C13.6498 13.9228 13.6498 14.1455 13.787 14.2828L15.2784 15.7742C15.4158 15.9114 15.6383 15.9114 15.7755 15.774Z" fill="#999999"/>
<path d="M12.3378 16.9062C12.1437 16.9062 11.9863 17.0636 11.9863 17.2577C11.9863 17.4519 12.1437 17.6092 12.3378 17.6092H14.447C14.641 17.6092 14.7984 17.4519 14.7984 17.2577C14.7984 17.0636 14.641 16.9062 14.447 16.9062H12.3378Z" fill="#999999"/>
<path d="M16.9062 12.2314V14.3405C16.9062 14.5346 17.0636 14.6921 17.2577 14.6921C17.4519 14.6921 17.6092 14.5346 17.6092 14.3405V12.2314C17.6092 12.0373 17.4519 11.8799 17.2577 11.8799C17.0636 11.8799 16.9062 12.0373 16.9062 12.2314Z" fill="#999999"/>
<path d="M24.227 24.2259C24.0897 24.3631 24.0897 24.5857 24.227 24.7231L25.7184 26.2145C25.7871 26.2831 25.877 26.3175 25.967 26.3175C26.2772 26.3175 26.4377 25.9397 26.2155 25.7173L24.7242 24.2259C24.5868 24.0887 24.3643 24.0887 24.227 24.2259Z" fill="#999999"/>
<path d="M23.0605 27.7673V25.6581C23.0605 25.464 22.903 25.3066 22.709 25.3066C22.5148 25.3066 22.3574 25.464 22.3574 25.6581V27.7673C22.3574 27.9614 22.5148 28.1187 22.709 28.1187C22.903 28.1187 23.0605 27.9614 23.0605 27.7673Z" fill="#999999"/>
<path d="M27.7693 23.0586C27.9633 23.0586 28.1207 22.9011 28.1207 22.707C28.1207 22.5128 27.9633 22.3555 27.7693 22.3555H25.66C25.466 22.3555 25.3086 22.5128 25.3086 22.707C25.3086 22.9011 25.466 23.0586 25.66 23.0586H27.7693Z" fill="#999999"/>
</g>
<defs>
<filter id="filter0_d_6702_36714" x="0" y="0" width="40" height="40" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="2.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_6702_36714"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_6702_36714" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,3 @@
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M43.2614 20.4808C44.1769 21.3963 44.1769 22.8804 43.2614 23.7955L27.5381 39.5192C26.6226 40.4343 25.139 40.4343 24.2235 39.5192L16.7386 32.0338C15.8231 31.1188 15.8231 29.6347 16.7386 28.7196C17.6537 27.8041 19.1377 27.8041 20.0528 28.7196L25.8806 34.5474L39.9467 20.4808C40.8623 19.5657 42.3463 19.5657 43.2614 20.4808ZM60 30C60 46.5825 46.5802 60 30 60C13.4175 60 0 46.5802 0 30C0 13.4175 13.4198 0 30 0C46.5825 0 60 13.4198 60 30ZM55.3125 30C55.3125 16.0085 43.9897 4.6875 30 4.6875C16.0085 4.6875 4.6875 16.0103 4.6875 30C4.6875 43.9915 16.0103 55.3125 30 55.3125C43.9915 55.3125 55.3125 43.9897 55.3125 30Z" fill="#023DFE" fill-opacity="0.7"/>
</svg>

After

Width:  |  Height:  |  Size: 761 B

1
firebase.json Normal file
View File

@ -0,0 +1 @@
{"flutter":{"platforms":{"android":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"test2-8a3d2","appId":"1:427332280600:ios:14346b200780dc760c7e6d","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"test2-8a3d2","configurations":{"android":"1:427332280600:android:2bc36fbe82994a3e0c7e6d","ios":"1:427332280600:ios:14346b200780dc760c7e6d","macos":"1:427332280600:ios:14346b200780dc760c7e6d","web":"1:427332280600:web:ad50516a87a35a1a0c7e6d","windows":"1:427332280600:web:f7a25537ccd5a7bd0c7e6d"}}}}}}

View File

@ -15,6 +15,7 @@
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */; };
F2A3345EC3021060731668D3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = B14AB50E8716720E10D074BD /* GoogleService-Info.plist */; };
FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
@ -64,6 +65,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B14AB50E8716720E10D074BD /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
D3AD250AADBF93406007C9EB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -138,6 +140,7 @@
331C8082294A63A400263BE5 /* RunnerTests */,
1454C118FFCECEEDF59152D2 /* Pods */,
20A3C64D2B1CFED5A81C3251 /* Frameworks */,
B14AB50E8716720E10D074BD /* GoogleService-Info.plist */,
);
sourceTree = "<group>";
};
@ -199,6 +202,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */,
7A77858F6F15CB76D2D3A872 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
);
buildRules = (
);
@ -264,6 +268,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
F2A3345EC3021060731668D3 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -303,6 +308,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
7A77858F6F15CB76D2D3A872 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\"";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:$HOME/.pub-cache/bin\"\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PODS_ROOT/FirebaseCrashlytics/upload-symbols\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw</string>
<key>GCM_SENDER_ID</key>
<string>427332280600</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.example.syncrowWeb</string>
<key>PROJECT_ID</key>
<string>test2-8a3d2</string>
<key>STORAGE_BUCKET</key>
<string>test2-8a3d2.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:427332280600:ios:14346b200780dc760c7e6d</string>
<key>DATABASE_URL</key>
<string>https://test2-8a3d2-default-rtdb.firebaseio.com</string>
</dict>
</plist>

View File

@ -1,160 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class DialogTextfieldDropdown extends StatefulWidget {
final List<String> items;
final ValueChanged<String> onSelected;
final String? initialValue;
const DialogTextfieldDropdown({
Key? key,
required this.items,
required this.onSelected,
this.initialValue,
}) : super(key: key);
@override
_DialogTextfieldDropdownState createState() =>
_DialogTextfieldDropdownState();
}
class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
bool _isOpen = false;
late OverlayEntry _overlayEntry;
final TextEditingController _controller = TextEditingController();
late List<String> _filteredItems; // Filtered items list
@override
void initState() {
super.initState();
_controller.text = widget.initialValue ?? 'Select Tag';
_filteredItems = List.from(widget.items); // Initialize filtered items
}
void _toggleDropdown() {
if (_isOpen) {
_closeDropdown();
} else {
_openDropdown();
}
}
void _openDropdown() {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry);
_isOpen = true;
}
void _closeDropdown() {
_overlayEntry.remove();
_isOpen = false;
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) {
return GestureDetector(
onTap: () {
_closeDropdown();
},
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height,
width: size.width,
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
constraints: const BoxConstraints(
maxHeight: 200.0,
),
child: ListView.builder(
shrinkWrap: true,
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
final item = _filteredItems[index];
return Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.lightGrayBorderColor,
width: 1.0,
),
),
),
child: ListTile(
title: Text(item,
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(
color: ColorsManager.textPrimaryColor)),
onTap: () {
_controller.text = item;
widget.onSelected(item);
setState(() {
_filteredItems
.remove(item); // Remove selected item
});
_closeDropdown();
},
),
);
},
),
),
),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _toggleDropdown,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.transparentColor),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextFormField(
controller: _controller,
onChanged: (value) {
setState(() {
_filteredItems = widget.items
.where((item) =>
item.toLowerCase().contains(value.toLowerCase()))
.toList(); // Filter items dynamically
});
widget.onSelected(value);
},
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
hintText: 'Enter or Select tag',
border: InputBorder.none,
),
),
),
const Icon(Icons.arrow_drop_down),
],
),
),
);
}
}

View File

@ -0,0 +1,185 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class TagDialogTextfieldDropdown extends StatefulWidget {
final List<Tag> items;
final ValueChanged<Tag> onSelected;
final Tag? initialValue;
final String product;
const TagDialogTextfieldDropdown({
Key? key,
required this.items,
required this.onSelected,
this.initialValue,
required this.product,
}) : super(key: key);
@override
_DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState();
}
class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
bool _isOpen = false;
OverlayEntry? _overlayEntry;
final TextEditingController _controller = TextEditingController();
final FocusNode _focusNode = FocusNode();
List<Tag> _filteredItems = [];
@override
void initState() {
super.initState();
_controller.text = widget.initialValue?.tag ?? '';
_filterItems();
_focusNode.addListener(() {
if (!_focusNode.hasFocus) {
_closeDropdown();
}
});
}
void _filterItems() {
setState(() {
_filteredItems = widget.items.where((tag) => tag.product?.uuid == widget.product).toList();
});
}
void _toggleDropdown() {
if (_isOpen) {
_closeDropdown();
} else {
_openDropdown();
}
}
void _openDropdown() {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry!);
_isOpen = true;
}
void _closeDropdown() {
if (_isOpen && _overlayEntry != null) {
_overlayEntry!.remove();
_overlayEntry = null;
_isOpen = false;
}
}
OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) {
return GestureDetector(
onTap: _closeDropdown,
behavior: HitTestBehavior.translucent,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: offset.dy + size.height,
width: size.width,
child: Material(
elevation: 4.0,
child: Container(
color: ColorsManager.whiteColors,
constraints: const BoxConstraints(maxHeight: 200.0),
child: StatefulBuilder(
builder: (context, setStateDropdown) {
return ListView.builder(
shrinkWrap: true,
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
final tag = _filteredItems[index];
return Container(
decoration: const BoxDecoration(
border: Border(
bottom: BorderSide(
color: ColorsManager.lightGrayBorderColor,
width: 1.0,
),
),
),
child: ListTile(
title: Text(tag.tag ?? '',
style: Theme.of(context)
.textTheme
.bodyMedium
?.copyWith(color: ColorsManager.textPrimaryColor)),
onTap: () {
_controller.text = tag.tag ?? '';
widget.onSelected(tag);
setState(() {
_filteredItems.remove(tag);
});
_closeDropdown();
},
),
);
},
);
},
),
),
),
),
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
behavior: HitTestBehavior.opaque,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.transparentColor),
borderRadius: BorderRadius.circular(8.0),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: TextFormField(
controller: _controller,
focusNode: _focusNode,
onFieldSubmitted: (value) {
final selectedTag = _filteredItems.firstWhere((tag) => tag.tag == value,
orElse: () => Tag(tag: value));
widget.onSelected(selectedTag);
_closeDropdown();
},
onTapOutside: (event) {
widget.onSelected(_filteredItems.firstWhere((tag) => tag.tag == _controller.text,
orElse: () => Tag(tag: _controller.text)));
_closeDropdown();
},
style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration(
hintText: 'Enter or Select a tag',
border: InputBorder.none,
),
),
),
GestureDetector(
onTap: _toggleDropdown,
child: const Icon(Icons.arrow_drop_down),
),
],
),
),
);
}
}

View File

@ -56,24 +56,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
children: [
Row(
children: [
// Checkbox with independent state management
Checkbox(
value: false,
onChanged: (bool? value) {
setState(() {});
},
side: WidgetStateBorderSide.resolveWith((states) {
return const BorderSide(color: ColorsManager.grayBorder);
}),
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return ColorsManager.grayBorder;
} else {
return ColorsManager.checkBoxFillColor;
}
}),
checkColor: ColorsManager.whiteColors,
),
// Expand/collapse icon, now wrapped in a GestureDetector for specific onTap
if (widget.children != null && widget.children!.isNotEmpty)
GestureDetector(
@ -84,7 +66,9 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
});
},
child: Icon(
_isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right,
_isExpanded
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_right,
color: ColorsManager.lightGrayColor,
size: 16.0, // Adjusted size for better alignment
),
@ -101,8 +85,10 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
_capitalizeFirstLetter(widget.title),
style: TextStyle(
color: widget.isSelected
? ColorsManager.blackColor // Change color to black when selected
: ColorsManager.lightGrayColor, // Gray when not selected
? ColorsManager
.blackColor // Change color to black when selected
: ColorsManager
.lightGrayColor, // Gray when not selected
fontWeight: FontWeight.w400,
),
),
@ -111,9 +97,11 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
],
),
// The expanded section (children) that shows when the tile is expanded
if (_isExpanded && widget.children != null && widget.children!.isNotEmpty)
if (_isExpanded &&
widget.children != null &&
widget.children!.isNotEmpty)
Padding(
padding: const EdgeInsets.only(left: 48.0), // Indented children
padding: const EdgeInsets.only(left: 24.0), // Indented children
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: widget.children!,

View File

@ -3,18 +3,33 @@ import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
class CustomSearchBar extends StatelessWidget {
class CustomSearchBar extends StatefulWidget {
final TextEditingController? controller;
final String hintText;
final String? searchQuery;
final Function(String)? onSearchChanged; // Callback for search input changes
const CustomSearchBar({
super.key,
this.controller,
this.searchQuery = '',
this.hintText = 'Search',
this.onSearchChanged,
});
@override
State<CustomSearchBar> createState() => _CustomSearchBarState();
}
class _CustomSearchBarState extends State<CustomSearchBar> {
@override
void dispose() {
if (widget.controller != null) {
widget.controller!.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
@ -36,16 +51,17 @@ class CustomSearchBar extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
),
child: TextField(
controller: controller,
child: TextFormField(
controller: widget.controller,
initialValue: widget.searchQuery,
style: const TextStyle(
color: Colors.black,
),
onChanged: onSearchChanged, // Call the callback on text change
onChanged: widget.onSearchChanged, // Call the callback on text change
decoration: InputDecoration(
filled: true,
fillColor: ColorsManager.textFieldGreyColor,
hintText: hintText,
hintText: widget.hintText,
hintStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: ColorsManager.lightGrayColor,
fontSize: 12,

View File

@ -1,25 +0,0 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
class SpacesSideTree extends StatefulWidget {
final List<CommunityModel> communities;
final String? selectedSpaceUuid;
const SpacesSideTree({
super.key,
required this.communities,
this.selectedSpaceUuid,
});
@override
State<SpacesSideTree> createState() => _SpacesSideTreeState();
}
class _SpacesSideTreeState extends State<SpacesSideTree> {
String _searchQuery = '';
String? _selectedSpaceUuid;
String? _selectedId;
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View File

@ -0,0 +1,93 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptionsDev {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
case TargetPlatform.windows:
return windows;
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyCVEvKsJYzhWDFM-9Od68FE0nPpP933st0',
appId: '1:427332280600:web:ad50516a87a35a1a0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
authDomain: 'test2-8a3d2.firebaseapp.com',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
measurementId: 'G-Z1RTTTV5H9',
);
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyA5qOErxdm0zJmoHIB0TixfebYEsNRpwV0',
appId: '1:427332280600:android:2bc36fbe82994a3e0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
iosBundleId: 'com.example.syncrowWeb',
);
static const FirebaseOptions macos = FirebaseOptions(
apiKey: 'AIzaSyABnpH6yo2RRjtkp4PlvtK84hKwRm2DhBw',
appId: '1:427332280600:ios:14346b200780dc760c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
iosBundleId: 'com.example.syncrowWeb',
);
static const FirebaseOptions windows = FirebaseOptions(
apiKey: 'AIzaSyDizKjPC5rdkEjDxwXjM-RU5unB0Ziq3iw',
appId: '1:427332280600:web:f7a25537ccd5a7bd0c7e6d',
messagingSenderId: '427332280600',
projectId: 'test2-8a3d2',
authDomain: 'test2-8a3d2.firebaseapp.com',
databaseURL: 'https://test2-8a3d2-default-rtdb.firebaseio.com',
storageBucket: 'test2-8a3d2.firebasestorage.app',
measurementId: 'G-4LFVXEXWKY',
);
}

View File

@ -0,0 +1,77 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptionsStaging {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyDP9GpYfLE8gHTj3kZ1hW8fx_FkJqOqSQk',
appId: '1:786692570726:android:0ef7079c2b978d4417b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyAWlRiuJ75FMlf2_UDdri1voWKvkaSHtRg',
appId: '1:786692570726:ios:455a6fcff77e130f17b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
iosBundleId: 'com.example.syncrow.app',
);
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSyDyGaQ3sZhb4meaY6sGke-YglhdhJ2is8Q',
appId: '1:786692570726:web:93c931e6701797b317b7a7',
messagingSenderId: '786692570726',
projectId: 'syncrow-staging',
authDomain: 'syncrow-staging.firebaseapp.com',
databaseURL: 'https://syncrow-staging-default-rtdb.firebaseio.com',
storageBucket: 'syncrow-staging.appspot.com',
measurementId: 'G-CZ3J3G6LMQ',
);
}

View File

@ -1,13 +1,14 @@
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:flutter_secure_storage/flutter_secure_storage.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/common/bloc/project_cubit.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';
@ -15,25 +16,25 @@ 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');
String.fromEnvironment('FLAVOR', defaultValue: 'production');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsStaging.currentPlatform,
);
initialSetup();
} catch (_) {}
final storage = FlutterSecureStorage();
final projectCubit = ProjectCubit(storage);
runApp(MyApp(projectCubit: projectCubit));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final ProjectCubit projectCubit;
MyApp({super.key, required this.projectCubit});
MyApp({super.key});
final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth,
@ -54,17 +55,19 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (context) => projectCubit),
BlocProvider<CreateRoutineBloc>(
create: (context) => CreateRoutineBloc(),
),
BlocProvider(
create: (context) => HomeBloc(projectCubit)..add(const FetchUserInfo())),
create: (context) => HomeBloc()..add(const FetchUserInfo())),
BlocProvider<VisitorPasswordBloc>(
create: (context) => VisitorPasswordBloc(projectCubit),
create: (context) => VisitorPasswordBloc(),
),
BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(projectCubit),
create: (context) => RoutineBloc(),
),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc(projectCubit)..add(InitialEvent()),
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
),
],
child: MaterialApp.router(
@ -77,6 +80,8 @@ class MyApp extends StatelessWidget {
PointerDeviceKind.unknown,
},
),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme,
routerConfig: _router,
));

88
lib/main_dev.dart Normal file
View File

@ -0,0 +1,88 @@
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_dev.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/theme/theme.dart';
Future<void> main() async {
try {
const environment =
String.fromEnvironment('FLAVOR', defaultValue: 'development');
await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsDev.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,
},
),
theme: myTheme,
routerConfig: _router,
));
}
}

View File

@ -3,18 +3,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/access_management/model/password_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
class AccessBloc extends Bloc<AccessEvent, AccessState> {
final ProjectCubit projectCubit;
AccessBloc(this.projectCubit) : super((AccessInitial())) {
AccessBloc() : super((AccessInitial())) {
on<FetchTableData>(_onFetchTableData);
on<SelectTime>(selectTime);
on<FilterDataEvent>(_filterData);
@ -34,10 +31,9 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
Future<void> _onFetchTableData(
FetchTableData event, Emitter<AccessState> emit) async {
try {
final projectUuid = projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(AccessLoaded());
data = await AccessMangApi()
.fetchVisitorPassword(projectUuid ?? TempConst.projectId);
data = await AccessMangApi().fetchVisitorPassword(projectUuid);
filteredData = data;
updateTabsCount();
emit(TableLoaded(data));

View File

@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart';
import 'package:syncrow_web/pages/common/custom_table.dart';
@ -18,6 +17,7 @@ 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';
import 'package:syncrow_web/utils/style.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -28,19 +28,19 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
final isLargeScreen = isLargeScreenSize(context);
final isSmallScreen = isSmallScreenSize(context);
final isHalfMediumScreen = isHafMediumScreenSize(context);
final padding = isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15);
final padding =
isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15);
return WebScaffold(
enableMenuSidebar: false,
appBarTitle: FittedBox(
child: Text(
'Access Management',
style: Theme.of(context).textTheme.headlineLarge,
),
appBarTitle: Text(
'Access Management',
style: ResponsiveTextTheme.of(context).deviceManagementTitle,
),
rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocProvider(
create: (BuildContext context) => AccessBloc(context.read<ProjectCubit>())..add(FetchTableData()),
create: (BuildContext context) =>
AccessBloc()..add(FetchTableData()),
child: BlocConsumer<AccessBloc, AccessState>(
listener: (context, state) {},
builder: (context, state) {
@ -94,11 +94,14 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
return [
item.passwordName,
item.passwordType.value,
accessBloc.timestampToDate(item.effectiveTime),
accessBloc.timestampToDate(item.invalidTime),
accessBloc
.timestampToDate(item.effectiveTime),
accessBloc
.timestampToDate(item.invalidTime),
item.deviceName.toString(),
item.authorizerEmail.toString(),
accessBloc.timestampToDate(item.invalidTime),
accessBloc
.timestampToDate(item.invalidTime),
item.passwordStatus.value,
];
}).toList(),
@ -109,7 +112,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
})));
}
Wrap _buildVisitorAdminPasswords(BuildContext context, AccessBloc accessBloc) {
Wrap _buildVisitorAdminPasswords(
BuildContext context, AccessBloc accessBloc) {
return Wrap(
spacing: 10,
runSpacing: 10,
@ -135,7 +139,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
borderRadius: 8,
child: Text(
'Create Visitor Password ',
style: context.textTheme.titleSmall!.copyWith(color: Colors.white, fontSize: 12),
style: context.textTheme.titleSmall!
.copyWith(color: Colors.white, fontSize: 12),
)),
),
// Container(
@ -173,8 +178,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '',
onSubmitted: (value) {
accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
emailAuthorizer:
accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp));
@ -192,8 +199,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '',
onSubmitted: (value) {
accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
emailAuthorizer:
accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp));
@ -222,7 +231,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
onSearch: () {
accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp));
@ -250,8 +260,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '',
onSubmitted: (value) {
accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
emailAuthorizer:
accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp));
@ -275,7 +287,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
onSearch: () {
accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp));

View File

@ -9,7 +9,9 @@ import 'package:syncrow_web/pages/auth/model/login_with_email_model.dart';
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_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.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';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
@ -32,8 +34,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
////////////////////////////// forget password //////////////////////////////////
final TextEditingController forgetEmailController = TextEditingController();
final TextEditingController forgetPasswordController =
TextEditingController();
final TextEditingController forgetPasswordController = TextEditingController();
final TextEditingController forgetOtp = TextEditingController();
final forgetFormKey = GlobalKey<FormState>();
final forgetEmailKey = GlobalKey<FormState>();
@ -50,8 +51,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
return;
}
_remainingTime = 1;
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
try {
forgetEmailValidate = '';
_remainingTime = (await AuthenticationAPI.sendOtp(
@ -88,8 +88,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
_timer?.cancel();
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
} else {
add(UpdateTimerEvent(
remainingTime: _remainingTime, isButtonEnabled: false));
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
}
});
}
@ -99,8 +98,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
}
Future<void> changePassword(
ChangePasswordEvent event, Emitter<AuthState> emit) async {
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
emit(LoadingForgetState());
try {
var response = await AuthenticationAPI.verifyOtp(
@ -116,8 +114,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
} on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage =
errorData['error']['message'] ?? 'something went wrong';
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
validate = errorMessage;
emit(AuthInitialState());
}
@ -131,9 +128,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
emit(TimerState(
isButtonEnabled: event.isButtonEnabled,
remainingTime: event.remainingTime));
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
}
///////////////////////////////////// login /////////////////////////////////////
@ -183,15 +178,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
if (token.accessTokenIsNotEmpty) {
FlutterSecureStorage storage = const FlutterSecureStorage();
await storage.write(
key: Token.loginAccessTokenKey, value: token.accessToken);
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
const FlutterSecureStorage().write(
key: UserModel.userUuidKey,
value: Token.decodeToken(token.accessToken)['uuid'].toString());
user = UserModel.fromToken(token);
loginEmailController.clear();
loginPasswordController.clear();
debugPrint("token " + token.accessToken);
emit(LoginSuccess());
} else {
emit(const LoginFailure(error: 'Something went wrong'));
@ -342,14 +335,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
static Future<String> getTokenAndValidate() async {
try {
const storage = FlutterSecureStorage();
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
StringsManager.firstLaunch) ??
true;
final firstLaunch =
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
if (firstLaunch) {
storage.deleteAll();
}
await SharedPreferencesHelper.saveBoolToSP(
StringsManager.firstLaunch, false);
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
if (value.isEmpty) {
return 'Token not found';
@ -402,9 +393,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
final String formattedTime = [
if (days > 0) '${days}d', // Append 'd' for days
if (days > 0 || hours > 0)
hours
.toString()
.padLeft(2, '0'), // Show hours if there are days or hours
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
minutes.toString().padLeft(2, '0'),
seconds.toString().padLeft(2, '0'),
].join(':');
@ -442,13 +431,10 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(LoginInitial());
}
static Future<void> logout(
BuildContext context, ProjectCubit projectCubit) async {
static Future<void> logout(BuildContext context) async {
final storage = FlutterSecureStorage();
await storage.delete(key: ProjectCubit.projectKey);
projectCubit.clearProjectUUID();
ProjectManager.clearProjectUUID();
context.read<SpaceTreeBloc>().add(ClearAllData());
storage.deleteAll();
}
}

View File

@ -8,6 +8,8 @@ class ForgetPasswordPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const ResponsiveLayout(
desktopBody: ForgetPasswordWebPage(), mobileBody: ForgetPasswordWebPage());
tablet: ForgetPasswordWebPage(),
desktopBody: ForgetPasswordWebPage(),
mobileBody: ForgetPasswordWebPage());
}
}

View File

@ -9,6 +9,8 @@ class LoginPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const ResponsiveLayout(
desktopBody: LoginWebPage(), mobileBody: LoginWebPage());
tablet: LoginWebPage(),
desktopBody: LoginWebPage(),
mobileBody: LoginWebPage());
}
}

View File

@ -1,19 +0,0 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class ProjectCubit extends Cubit<String?> {
final FlutterSecureStorage storage;
static const String projectKey = "selected_project_uuid";
ProjectCubit(this.storage) : super(null);
Future<void> setProjectUUID(String newUUID) async {
await storage.write(key: projectKey, value: newUUID);
emit(newUUID);
}
Future<void> clearProjectUUID() async {
await storage.delete(key: projectKey);
emit(null);
}
}

View File

@ -0,0 +1,19 @@
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class ProjectManager {
static Future<String?> getProjectUUID() async {
final projectUuid = await SharedPreferencesHelper.readStringFromSP(
StringsManager.projectKey);
return projectUuid.isNotEmpty ? projectUuid : null;
}
static Future<void> setProjectUUID(String newUUID) async {
await SharedPreferencesHelper.saveStringToSP(
StringsManager.projectKey, newUUID);
}
static Future<void> clearProjectUUID() async {
await SharedPreferencesHelper.removeValueFromSP(StringsManager.projectKey);
}
}

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
@ -19,6 +20,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
on<AcControlEvent>(_onAcControl);
on<AcBatchControlEvent>(_onAcBatchControl);
on<AcFactoryResetEvent>(_onFactoryReset);
on<AcStatusUpdated>(_onAcStatusUpdated);
}
FutureOr<void> _onFetchAcStatus(
@ -28,12 +30,64 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ACStatusLoaded(deviceStatus));
} catch (e) {
emit(AcsFailedState(error: e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) async {
if (event.snapshot.value == null) return;
if (_timer != null) {
await Future.delayed(const Duration(seconds: 1));
}
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
AcStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(AcStatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
deviceStatus = event.deviceStatus;
emit(ACStatusLoaded(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);

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
sealed class AcsEvent extends Equatable {
@ -7,6 +8,7 @@ sealed class AcsEvent extends Equatable {
@override
List<Object> get props => [];
}
class AcUpdated extends AcsEvent {}
class AcFetchDeviceStatusEvent extends AcsEvent {
final String deviceId;
@ -16,7 +18,10 @@ 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;

View File

@ -24,7 +24,8 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);
return BlocProvider(
create: (context) => AcBloc(deviceId: device.uuid!)..add(AcFetchDeviceStatusEvent(device.uuid!)),
create: (context) => AcBloc(deviceId: device.uuid!)
..add(AcFetchDeviceStatusEvent(device.uuid!)),
child: BlocBuilder<AcBloc, AcsState>(
builder: (context, state) {
if (state is ACStatusLoaded) {
@ -98,7 +99,8 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
),
Text(
'h',
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor),
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor),
),
Text(
'30',
@ -107,7 +109,9 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
fontWeight: FontWeight.bold,
),
),
Text('m', style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor)),
Text('m',
style: context.textTheme.bodySmall!
.copyWith(color: ColorsManager.blackColor)),
IconButton(
padding: const EdgeInsets.all(0),
onPressed: () {},

View File

@ -1,11 +1,10 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
part 'device_managment_event.dart';
part 'device_managment_state.dart';
@ -22,9 +21,8 @@ class DeviceManagementBloc
String currentProductName = '';
String? currentCommunity;
String? currentUnitName;
final ProjectCubit projectCubit;
DeviceManagementBloc(this.projectCubit) : super(DeviceManagementInitial()) {
DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices);
on<FilterDevices>(_onFilterDevices);
on<SelectedFilterChanged>(_onSelectedFilterChanged);
@ -42,18 +40,17 @@ class DeviceManagementBloc
List<AllDevicesModel> devices = [];
_devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>();
final projectUuid = projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = await DevicesManagementApi()
.fetchDevices('', '', projectUuid ?? TempConst.projectId);
devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
} else {
for (var community in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
for (var space in spacesList) {
devices.addAll(await DevicesManagementApi().fetchDevices(
community, space, projectUuid ?? TempConst.projectId));
community, space, projectUuid));
}
}
}

View File

@ -8,6 +8,7 @@ import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart';
@ -148,7 +149,9 @@ class AllDevicesModel {
productName = json['productName']?.toString();
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();
}
}
@ -196,7 +199,8 @@ 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;
@ -226,6 +230,8 @@ SOS
// tempIcon = Assets.gang3touch;
} else if (type == DeviceType.WaterLeak) {
tempIcon = Assets.waterLeakNormal;
} else if (type == DeviceType.WaterLeak) {
tempIcon = Assets.waterLeakNormal;
} else {
tempIcon = Assets.logoHorizontal;
}
@ -241,37 +247,77 @@ SOS
switch (productType) {
case 'AC':
return [
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
TempSetFunction(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'),
//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'),
];
default:
return [];
}

View File

@ -2,14 +2,17 @@ import 'package:flutter/foundation.dart';
class FactoryResetModel {
final List<String> devicesUuid;
final String operationType;
FactoryResetModel({
required this.devicesUuid,
this.operationType = "RESET",
});
factory FactoryResetModel.fromJson(Map<String, dynamic> json) {
return FactoryResetModel(
devicesUuid: List<String>.from(json['devicesUuid']),
operationType: "RESET",
);
}

View File

@ -1,15 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.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/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routines/view/routines_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -20,17 +22,17 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => DeviceManagementBloc(context.read<ProjectCubit>())..add(FetchDevices(context)),
create: (context) =>
DeviceManagementBloc()..add(FetchDevices(context)),
),
],
child: WebScaffold(
appBarTitle: FittedBox(
child: Text(
'Device Management',
style: Theme.of(context).textTheme.headlineLarge,
),
appBarTitle: Text(
'Device Management',
style: ResponsiveTextTheme.of(context).deviceManagementTitle,
),
centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
centerBody:
BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
@ -39,15 +41,24 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null,
),
onPressed: () {
BlocProvider.of<CreateRoutineBloc>(context)
.add(const ResetSelectedEvent());
context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context
.read<DeviceManagementBloc>()
.add(FetchDevices(context));
},
child: Text(
'Devices',
style: context.textTheme.titleMedium?.copyWith(
color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400,
color: !state.routineTab
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
!state.routineTab ? FontWeight.w700 : FontWeight.w400,
),
),
),
@ -56,13 +67,21 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null,
),
onPressed: () {
context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true));
BlocProvider.of<CreateRoutineBloc>(context)
.add(const ResetSelectedEvent());
context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: true));
},
child: Text(
'Routines',
style: context.textTheme.titleMedium?.copyWith(
color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor,
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400,
color: state.routineTab
? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
state.routineTab ? FontWeight.w700 : FontWeight.w400,
),
),
),
@ -70,7 +89,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
);
}),
rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
scaffoldBody:
BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
if (state.routineTab) {
return const RoutinesView();
}
@ -85,7 +105,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
} else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) {
return DeviceManagementBody(devices: deviceState.filteredDevices);
return DeviceManagementBody(
devices: deviceState.filteredDevices);
} else {
return const Center(child: Text('Error fetching Devices'));
}

View File

@ -8,7 +8,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
@ -95,7 +94,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
const DeviceSearchFilters(),
const SizedBox(height: 12),
Container(
height: 45,
// height: 45,
width: 125,
decoration: containerDecoration,
child: Center(

View File

@ -12,77 +12,68 @@ class DeviceSearchFilters extends StatefulWidget {
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
}
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
final TextEditingController communityController = TextEditingController();
final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController();
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
with HelperResponsiveLayout {
final _unitNameController = TextEditingController();
final _productNameController = TextEditingController();
List<Widget> get _widgets => [
_buildSearchField("Space Name", _unitNameController, 200),
_buildSearchField("Device Name / Product Name", _productNameController, 300),
_buildSearchResetButtons(),
];
@override
Widget build(BuildContext context) {
return isExtraLargeScreenSize(context)
? Row(
children: [
_buildSearchField("Community", communityController, 200),
const SizedBox(width: 20),
_buildSearchField("Space Name", unitNameController, 200),
const SizedBox(width: 20),
_buildSearchField("Device Name / Product Name", productNameController, 300),
const SizedBox(width: 20),
_buildSearchResetButtons(),
],
)
: Wrap(
spacing: 20,
runSpacing: 10,
children: [
_buildSearchField(
"Community",
communityController,
200,
),
_buildSearchField("Space Name", unitNameController, 200),
_buildSearchField(
"Device Name / Product Name",
productNameController,
300,
),
_buildSearchResetButtons(),
],
);
if (isExtraLargeScreenSize(context)) {
return Row(
children: _widgets
.map((e) => Padding(padding: const EdgeInsets.all(10), child: e))
.toList(),
);
}
return Wrap(
spacing: 20,
runSpacing: 10,
children: _widgets,
);
}
Widget _buildSearchField(String title, TextEditingController controller, double width) {
return Container(
child: StatefulTextField(
title: title,
width: width,
elevation: 2,
controller: controller,
onSubmitted: () {
context.read<DeviceManagementBloc>().add(SearchDevices(
productName: productNameController.text,
unitName: unitNameController.text,
community: communityController.text,
searchField: true));
},
onChanged: (p0) {},
),
Widget _buildSearchField(
String title,
TextEditingController controller,
double width,
) {
return StatefulTextField(
title: title,
width: width,
elevation: 2,
controller: controller,
onSubmitted: () {
final searchDevicesEvent = SearchDevices(
productName: _productNameController.text,
unitName: _unitNameController.text,
searchField: true,
);
context.read<DeviceManagementBloc>().add(searchDevicesEvent);
},
onChanged: (p0) {},
);
}
Widget _buildSearchResetButtons() {
return SearchResetButtons(
onSearch: () {
context.read<DeviceManagementBloc>().add(SearchDevices(
community: communityController.text,
unitName: unitNameController.text,
productName: productNameController.text,
searchField: true));
},
onSearch: () => context.read<DeviceManagementBloc>().add(
SearchDevices(
unitName: _unitNameController.text,
productName: _productNameController.text,
searchField: true,
),
),
onReset: () {
communityController.clear();
unitNameController.clear();
productNameController.clear();
_unitNameController.clear();
_productNameController.clear();
context.read<DeviceManagementBloc>()
..add(ResetFilters())
..add(FetchDevices(context));

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
@ -22,42 +23,55 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
on<ShowCeilingDescriptionEvent>(_showDescription);
on<BackToCeilingGridViewEvent>(_backToGridView);
on<CeilingFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
void _fetchCeilingSensorStatus(
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingInitialState());
try {
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId);
var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
// _listenToChanges();
_listenToChanges(event.deviceId);
} catch (e) {
emit(CeilingFailedState(error: e.toString()));
return;
}
}
// _listenToChanges() {
// try {
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
// List<StatusModel> statusList = [];
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
// usersMap['status'].forEach((element) {
// statusList.add(StatusModel(code: element['code'], value: element['value']));
// });
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
// deviceStatus = WallSensorModel.fromJson(statusList);
// add(WallSensorUpdatedEvent());
// });
// } catch (_) {}
// }
deviceStatus = CeilingSensorModel.fromJson(statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _changeValue(CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
void _onStatusUpdated(StatusUpdated event, Emitter<CeilingSensorState> emit) {
deviceStatus = event.deviceStatus;
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
}
void _changeValue(
CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
if (event.code == 'sensitivity') {
deviceStatus.sensitivity = event.value;
@ -122,7 +136,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
@ -143,8 +158,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
});
}
FutureOr<void> _getDeviceReports(
GetCeilingDeviceReportsEvent event, Emitter<CeilingSensorState> emit) async {
FutureOr<void> _getDeviceReports(GetCeilingDeviceReportsEvent event,
Emitter<CeilingSensorState> emit) async {
if (event.code.isEmpty) {
emit(ShowCeilingDescriptionState(description: reportString));
return;
@ -155,7 +170,8 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
try {
// await DevicesManagementApi.getDeviceReportsByDate(deviceId, event.code, from.toString(), to.toString())
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) {
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(CeilingReportsState(deviceReport: value));
});
} catch (e) {
@ -165,19 +181,23 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
}
}
void _showDescription(ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
void _showDescription(
ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
emit(ShowCeilingDescriptionState(description: event.description));
}
void _backToGridView(BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
void _backToGridView(
BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
}
FutureOr<void> _fetchCeilingSensorBatchControl(
CeilingFetchDeviceStatusEvent event, Emitter<CeilingSensorState> emit) async {
CeilingFetchDeviceStatusEvent event,
Emitter<CeilingSensorState> emit) async {
emit(CeilingLoadingInitialState());
try {
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = CeilingSensorModel.fromJson(response.status);
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
} catch (e) {

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/ceiling_sensor/model/ceiling_sensor_model.dart';
abstract class CeilingSensorEvent extends Equatable {
const CeilingSensorEvent();
@ -83,3 +84,12 @@ class CeilingFactoryResetEvent extends CeilingSensorEvent {
@override
List<Object> get props => [devicesId, factoryResetModel];
}
class StatusUpdated extends CeilingSensorEvent {
final CeilingSensorModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
@ -16,6 +17,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
on<CurtainControl>(_onCurtainControl);
on<CurtainBatchControl>(_onCurtainBatchControl);
on<CurtainFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
FutureOr<void> _onFetchDeviceStatus(
@ -24,7 +26,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
try {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus = _checkStatus(status.status[0].value);
emit(CurtainStatusLoaded(deviceStatus));
@ -33,6 +35,48 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
}
}
void _listenToChanges(String deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
List<Status> statusList = [];
if (data['status'] != null) {
for (var element in data['status']) {
statusList.add(
Status(
code: element['code'].toString(),
value: element['value'].toString(),
),
);
}
}
if (statusList.isNotEmpty) {
bool newStatus = _checkStatus(statusList[0].value);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
}
}
});
} catch (e) {
emit(CurtainError('Failed to listen to changes: $e'));
}
}
void _onStatusUpdated(StatusUpdated event, Emitter<CurtainState> emit) {
emit(CurtainStatusLoading());
deviceStatus = event.deviceStatus;
emit(CurtainStatusLoaded(deviceStatus));
}
FutureOr<void> _onCurtainControl(
CurtainControl event, Emitter<CurtainState> emit) async {
final oldValue = deviceStatus;

View File

@ -60,3 +60,7 @@ class CurtainFactoryReset extends CurtainEvent {
@override
List<Object> get props => [deviceId, factoryReset];
}
class StatusUpdated extends CurtainEvent {
final bool deviceStatus;
const StatusUpdated(this.deviceStatus);
}

View File

@ -1,6 +1,7 @@
// ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.dart';
@ -18,6 +19,39 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
//on<DoorLockControl>(_onDoorLockControl);
on<UpdateLockEvent>(_updateLock);
on<DoorLockFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
DoorLockStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<DoorLockState> emit) {
emit(DoorLockStatusLoading());
deviceStatus = event.deviceStatus;
emit(DoorLockStatusLoaded(deviceStatus));
}
FutureOr<void> _onFetchDeviceStatus(
@ -28,6 +62,8 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
DoorLockStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(DoorLockStatusLoaded(deviceStatus));
} catch (e) {
emit(DoorLockControlError(e.toString()));

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/door_lock/models/door_lock_status_model.dart';
sealed class DoorLockEvent extends Equatable {
const DoorLockEvent();
@ -51,3 +52,10 @@ class DoorLockFactoryReset extends DoorLockEvent {
@override
List<Object> get props => [deviceId, factoryReset];
}
class StatusUpdated extends DoorLockEvent {
final DoorLockStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:bloc/bloc.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/garage_door/bloc/garage_door_event.dart';
@ -39,31 +40,68 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
on<GarageDoorFetchBatchStatusEvent>(_onFetchBatchStatus);
on<GarageDoorFactoryResetEvent>(_onFactoryReset);
on<EditGarageDoorScheduleEvent>(_onEditSchedule);
on<StatusUpdated>(_onStatusUpdated);
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
GarageDoorStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _fetchGarageDoorStatus(GarageDoorInitialEvent event, Emitter<GarageDoorState> emit) async {
void _onStatusUpdated(StatusUpdated event, Emitter<GarageDoorState> emit) {
deviceStatus = event.deviceStatus;
emit(GarageDoorLoadedState(status: deviceStatus));
}
void _fetchGarageDoorStatus(
GarageDoorInitialEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState());
try {
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId);
var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status);
_listenToChanges(deviceId);
emit(GarageDoorLoadedState(status: deviceStatus));
} catch (e) {
emit(GarageDoorErrorState(message: e.toString()));
}
}
Future<void> _onFetchBatchStatus(GarageDoorFetchBatchStatusEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _onFetchBatchStatus(GarageDoorFetchBatchStatusEvent event,
Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState());
try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status);
final status =
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus =
GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status);
emit(GarageDoorBatchStatusLoaded(deviceStatus));
} catch (e) {
emit(GarageDoorBatchControlError(e.toString()));
}
}
Future<void> _addSchedule(AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _addSchedule(
AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try {
ScheduleEntry newSchedule = ScheduleEntry(
category: event.category,
@ -71,9 +109,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
);
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId);
bool success =
await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId);
if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'));
add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else {
emit(GarageDoorLoadedState(status: deviceStatus));
}
@ -82,16 +122,19 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
void _onUpdateCountdownAlarm(UpdateCountdownAlarmEvent event, Emitter<GarageDoorState> emit) {
void _onUpdateCountdownAlarm(
UpdateCountdownAlarmEvent event, Emitter<GarageDoorState> emit) {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(
status: currentState.status.copyWith(countdownAlarm: event.countdownAlarm),
status:
currentState.status.copyWith(countdownAlarm: event.countdownAlarm),
));
}
}
void _onUpdateTrTimeCon(UpdateTrTimeConEvent event, Emitter<GarageDoorState> emit) {
void _onUpdateTrTimeCon(
UpdateTrTimeConEvent event, Emitter<GarageDoorState> emit) {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(
@ -100,7 +143,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _updateSchedule(UpdateGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _updateSchedule(UpdateGarageDoorScheduleEvent event,
Emitter<GarageDoorState> emit) async {
try {
final updatedSchedules = deviceStatus.schedules?.map((schedule) {
if (schedule.scheduleId == event.scheduleId) {
@ -127,12 +171,15 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event,
Emitter<GarageDoorState> emit) async {
try {
bool success = await DevicesManagementApi().deleteScheduleRecord(deviceStatus.uuid, event.scheduleId);
bool success = await DevicesManagementApi()
.deleteScheduleRecord(deviceStatus.uuid, event.scheduleId);
if (success) {
final updatedSchedules =
deviceStatus.schedules?.where((schedule) => schedule.scheduleId != event.scheduleId).toList();
final updatedSchedules = deviceStatus.schedules
?.where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules);
emit(GarageDoorLoadedState(status: deviceStatus));
} else {
@ -143,11 +190,12 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event,
Emitter<GarageDoorState> emit) async {
emit(ScheduleGarageLoadingState());
try {
List<ScheduleModel> schedules =
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category);
List<ScheduleModel> schedules = await DevicesManagementApi()
.getDeviceSchedules(deviceStatus.uuid, event.category);
deviceStatus = deviceStatus.copyWith(schedules: schedules);
emit(
GarageDoorLoadedState(
@ -165,30 +213,37 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _updateSelectedTime(UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _updateSelectedTime(
UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(selectedTime: event.selectedTime));
}
}
Future<void> _updateSelectedDay(UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _updateSelectedDay(
UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
List<bool> updatedDays = List.from(currentState.selectedDays);
updatedDays[event.dayIndex] = event.isSelected;
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime));
emit(currentState.copyWith(
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
}
}
Future<void> _updateFunctionOn(UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _updateFunctionOn(
UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(functionOn: event.functionOn, selectedTime: currentState.selectedTime));
emit(currentState.copyWith(
functionOn: event.functionOn,
selectedTime: currentState.selectedTime));
}
}
Future<void> _initializeAddSchedule(InitializeAddScheduleEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _initializeAddSchedule(
InitializeAddScheduleEvent event, Emitter<GarageDoorState> emit) async {
final currentState = state;
if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(
@ -200,20 +255,25 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _fetchRecords(FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _fetchRecords(
FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorReportsLoadingState());
try {
final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
final from = DateTime.now()
.subtract(const Duration(days: 30))
.millisecondsSinceEpoch;
final to = DateTime.now().millisecondsSinceEpoch;
final DeviceReport records =
await DevicesManagementApi.getDeviceReportsByDate(event.deviceId, 'switch_1', from.toString(), to.toString());
await DevicesManagementApi.getDeviceReportsByDate(
event.deviceId, 'switch_1', from.toString(), to.toString());
emit(GarageDoorReportsState(deviceReport: records));
} catch (e) {
emit(GarageDoorReportsFailedState(error: e.toString()));
}
}
Future<void> _onBatchControl(GarageDoorBatchControlEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _onBatchControl(
GarageDoorBatchControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false;
_updateLocalValue(event.code, event.value);
@ -233,11 +293,13 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
void _backToGridView(BackToGarageDoorGridViewEvent event, Emitter<GarageDoorState> emit) {
void _backToGridView(
BackToGarageDoorGridViewEvent event, Emitter<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus));
}
void _handleUpdate(GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) {
void _handleUpdate(
GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus));
}
@ -253,9 +315,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
late bool status;
await Future.delayed(const Duration(milliseconds: 500));
if (isBatch) {
status = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
status = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
status = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value));
status = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!status) {
@ -270,10 +334,12 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
Future<void> _onFactoryReset(GarageDoorFactoryResetEvent event, Emitter<GarageDoorState> emit) async {
Future<void> _onFactoryReset(
GarageDoorFactoryResetEvent event, Emitter<GarageDoorState> emit) async {
emit(GarageDoorLoadingState());
try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) {
emit(const GarageDoorErrorState(message: 'Failed to reset device'));
} else {
@ -284,34 +350,47 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
void _increaseDelay(IncreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
void _increaseDelay(
IncreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
// if (deviceStatus.countdown1 != 0) {
try {
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay + Duration(minutes: 10));
deviceStatus = deviceStatus.copyWith(
delay: deviceStatus.delay + Duration(minutes: 10));
emit(GarageDoorLoadedState(status: deviceStatus));
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1'));
add(GarageDoorControlEvent(
deviceId: event.deviceId,
value: deviceStatus.delay.inSeconds,
code: 'countdown_1'));
} catch (e) {
emit(GarageDoorErrorState(message: e.toString()));
}
// }
}
void _decreaseDelay(DecreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
void _decreaseDelay(
DecreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
// if (deviceStatus.countdown1 != 0) {
try {
if (deviceStatus.delay.inMinutes > 10) {
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay - Duration(minutes: 10));
deviceStatus = deviceStatus.copyWith(
delay: deviceStatus.delay - Duration(minutes: 10));
}
emit(GarageDoorLoadedState(status: deviceStatus));
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1'));
add(GarageDoorControlEvent(
deviceId: event.deviceId,
value: deviceStatus.delay.inSeconds,
code: 'countdown_1'));
} catch (e) {
emit(GarageDoorErrorState(message: e.toString()));
}
//}
}
void _garageDoorControlEvent(GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'countdown_1' ? deviceStatus.countdown1 : deviceStatus.switch1;
void _garageDoorControlEvent(
GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'countdown_1'
? deviceStatus.countdown1
: deviceStatus.switch1;
_updateLocalValue(event.code, event.value);
emit(GarageDoorLoadedState(status: deviceStatus));
final success = await _runDeBouncer(
@ -327,7 +406,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
}
}
void _revertValue(String code, dynamic oldValue, Emitter<GarageDoorState> emit) {
void _revertValue(
String code, dynamic oldValue, Emitter<GarageDoorState> emit) {
switch (code) {
case 'switch_1':
if (oldValue is bool) {
@ -336,7 +416,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break;
case 'countdown_1':
if (oldValue is int) {
deviceStatus = deviceStatus.copyWith(countdown1: oldValue, delay: Duration(seconds: oldValue));
deviceStatus = deviceStatus.copyWith(
countdown1: oldValue, delay: Duration(seconds: oldValue));
}
break;
// Add other cases if needed
@ -358,7 +439,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break;
case 'countdown_1':
if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value, delay: Duration(seconds: value));
deviceStatus = deviceStatus.copyWith(
countdown1: value, delay: Duration(seconds: value));
}
break;
case 'countdown_alarm':
@ -401,7 +483,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
return super.close();
}
FutureOr<void> _onEditSchedule(EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
FutureOr<void> _onEditSchedule(
EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try {
ScheduleEntry newSchedule = ScheduleEntry(
scheduleId: event.scheduleId,
@ -410,9 +493,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
);
bool success = await DevicesManagementApi().editScheduleRecord(deviceId, newSchedule);
bool success = await DevicesManagementApi()
.editScheduleRecord(deviceId, newSchedule);
if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'));
add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else {
emit(GarageDoorLoadedState(status: deviceStatus));
}

View File

@ -3,6 +3,7 @@
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
abstract class GarageDoorEvent extends Equatable {
const GarageDoorEvent();
@ -25,7 +26,8 @@ class GarageDoorControlEvent extends GarageDoorEvent {
final dynamic value;
final String code;
const GarageDoorControlEvent({required this.deviceId, required this.value, required this.code});
const GarageDoorControlEvent(
{required this.deviceId, required this.value, required this.code});
@override
List<Object?> get props => [deviceId, value];
@ -121,7 +123,8 @@ class FetchGarageDoorRecordsEvent extends GarageDoorEvent {
final String deviceId;
final String code;
const FetchGarageDoorRecordsEvent({required this.deviceId, required this.code});
const FetchGarageDoorRecordsEvent(
{required this.deviceId, required this.code});
@override
List<Object?> get props => [deviceId, code];
@ -232,3 +235,10 @@ class GarageDoorFactoryResetEvent extends GarageDoorEvent {
@override
List<Object?> get props => [factoryReset, deviceId];
}
class StatusUpdated extends GarageDoorEvent {
final GarageDoorStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
@ -16,6 +17,7 @@ class MainDoorSensorBloc
on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus);
on<MainDoorSensorReportsEvent>(_fetchReports);
on<MainDoorSensorFactoryReset>(_factoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
late MainDoorSensorStatusModel deviceStatus;
@ -28,7 +30,7 @@ class MainDoorSensorBloc
final status = await DevicesManagementApi()
.getDeviceStatus(event.deviceId)
.then((value) => value.status);
_listenToChanges(event.deviceId);
deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status);
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
} catch (e) {
@ -156,4 +158,35 @@ class MainDoorSensorBloc
emit(MainDoorSensorFailedState(error: e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = MainDoorSensorStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<MainDoorSensorState> emit) {
deviceStatus = event.deviceStatus;
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
}
}

View File

@ -1,4 +1,5 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart';
import '../../all_devices/models/factory_reset_model.dart';
@ -71,3 +72,10 @@ class MainDoorSensorFactoryReset extends MainDoorSensorEvent {
MainDoorSensorFactoryReset(
{required this.deviceId, required this.factoryReset});
}
class StatusUpdated extends MainDoorSensorEvent {
final MainDoorSensorStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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';
@ -10,33 +11,71 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
part 'one_gang_glass_switch_event.dart';
part 'one_gang_glass_switch_state.dart';
class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
class OneGangGlassSwitchBloc
extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
OneGangGlassStatusModel deviceStatus;
Timer? _timer;
OneGangGlassSwitchBloc({required String deviceId})
: deviceStatus = OneGangGlassStatusModel(uuid: deviceId, switch1: false, countDown: 0),
: deviceStatus = OneGangGlassStatusModel(
uuid: deviceId, switch1: false, countDown: 0),
super(OneGangGlassSwitchInitial()) {
on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<OneGangGlassSwitchControl>(_onControl);
on<OneGangGlassSwitchBatchControl>(_onBatchControl);
on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<OneGangGlassFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
Future<void> _onFetchDeviceStatus(
OneGangGlassSwitchFetchDeviceEvent event, Emitter<OneGangGlassSwitchState> emit) async {
Future<void> _onFetchDeviceStatus(OneGangGlassSwitchFetchDeviceEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus =
OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(OneGangGlassSwitchError(e.toString()));
}
}
Future<void> _onControl(OneGangGlassSwitchControl event, Emitter<OneGangGlassSwitchState> emit) async {
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = OneGangGlassStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<OneGangGlassSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
}
Future<void> _onControl(OneGangGlassSwitchControl event,
Emitter<OneGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
@ -52,10 +91,12 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
);
}
Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event, Emitter<OneGangGlassSwitchState> emit) async {
Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading());
try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) {
emit(OneGangGlassSwitchError('Failed to reset device'));
} else {
@ -66,7 +107,8 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
}
}
Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event, Emitter<OneGangGlassSwitchState> emit) async {
Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event,
Emitter<OneGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
@ -83,11 +125,14 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
}
Future<void> _onFetchBatchStatus(
OneGangGlassSwitchFetchBatchStatusEvent event, Emitter<OneGangGlassSwitchState> emit) async {
OneGangGlassSwitchFetchBatchStatusEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status);
final status =
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = OneGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(OneGangGlassSwitchError(e.toString()));
@ -117,9 +162,11 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value));
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!response) {
@ -131,7 +178,8 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter<OneGangGlassSwitchState> emit) {
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<OneGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
}

View File

@ -39,6 +39,11 @@ class OneGangGlassSwitchFetchBatchStatusEvent extends OneGangGlassSwitchEvent {
OneGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
}
class StatusUpdated extends OneGangGlassSwitchEvent {
final OneGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
}
class OneGangGlassFactoryResetEvent extends OneGangGlassSwitchEvent {
final FactoryResetModel factoryReset;
final String deviceId;

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
@ -16,6 +17,7 @@ class WallLightSwitchBloc
on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<WallLightSwitchBatchControl>(_onBatchControl);
on<WallLightFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
late WallLightStatusModel deviceStatus;
@ -31,12 +33,44 @@ class WallLightSwitchBloc
deviceStatus =
WallLightStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(WallLightSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(WallLightSwitchError(e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
WallLightStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<WallLightSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(WallLightSwitchStatusLoaded(deviceStatus));
}
FutureOr<void> _onControl(
WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);

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/one_gang_switch/models/wall_light_status_model.dart';
class WallLightSwitchEvent extends Equatable {
@override
@ -57,3 +58,10 @@ class WallLightFactoryReset extends WallLightSwitchEvent {
@override
List<Object> get props => [deviceId, factoryReset];
}
class StatusUpdated extends WallLightSwitchEvent {
final WallLightStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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';
@ -10,7 +11,8 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
part 'three_gang_glass_switch_event.dart';
part 'three_gang_glass_switch_state.dart';
class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
class ThreeGangGlassSwitchBloc
extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
ThreeGangGlassStatusModel deviceStatus;
Timer? _timer;
@ -29,21 +31,57 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
on<ThreeGangGlassSwitchBatchControl>(_onBatchControl);
on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<ThreeGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
Future<void> _onFetchDeviceStatus(
ThreeGangGlassSwitchFetchDeviceEvent event, Emitter<ThreeGangGlassSwitchState> emit) async {
Future<void> _onFetchDeviceStatus(ThreeGangGlassSwitchFetchDeviceEvent event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(ThreeGangGlassSwitchError(e.toString()));
}
}
Future<void> _onControl(ThreeGangGlassSwitchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = ThreeGangGlassStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(
StatusUpdated event, Emitter<ThreeGangGlassSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
}
Future<void> _onControl(ThreeGangGlassSwitchControl event,
Emitter<ThreeGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
@ -59,7 +97,8 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
);
}
Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event,
Emitter<ThreeGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value);
@ -76,21 +115,26 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
}
Future<void> _onFetchBatchStatus(
ThreeGangGlassSwitchFetchBatchStatusEvent event, Emitter<ThreeGangGlassSwitchState> emit) async {
ThreeGangGlassSwitchFetchBatchStatusEvent event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading());
try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status);
final status =
await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = ThreeGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) {
emit(ThreeGangGlassSwitchError(e.toString()));
}
}
Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event, Emitter<ThreeGangGlassSwitchState> emit) async {
Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading());
try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) {
emit(ThreeGangGlassSwitchError('Failed'));
} else {
@ -124,9 +168,11 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value));
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
}
if (!response) {
@ -138,7 +184,8 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
});
}
void _revertValueAndEmit(String deviceId, String code, bool oldValue, Emitter<ThreeGangGlassSwitchState> emit) {
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
Emitter<ThreeGangGlassSwitchState> emit) {
_updateLocalValue(code, oldValue);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
}

View File

@ -49,3 +49,10 @@ class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
required this.factoryReset,
});
}
class StatusUpdated extends ThreeGangGlassSwitchEvent {
final ThreeGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -3,6 +3,7 @@
import 'dart:async';
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_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
@ -22,6 +23,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
on<LivingRoomBatchControl>(_livingRoomBatchControl);
on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl);
on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
@ -32,6 +34,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
LivingRoomStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(deviceId);
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) {
emit(LivingRoomDeviceManagementError(e.toString()));
@ -144,6 +147,9 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus =
LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status);
// for (var deviceId in event.devicesIds) {
// _listenToChanges(deviceId);
// }
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) {
emit(LivingRoomDeviceManagementError(e.toString()));
@ -185,4 +191,34 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
emit(LivingRoomDeviceManagementError(e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
LivingRoomStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<LivingRoomState> emit) {
deviceStatus = event.deviceStatus;
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
}
}

View File

@ -58,3 +58,10 @@ class LivingRoomFactoryResetEvent extends LivingRoomEvent {
@override
List<Object> get props => [uuid, factoryReset];
}
class StatusUpdated extends LivingRoomEvent {
final LivingRoomStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,12 +1,11 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart';
@ -14,7 +13,6 @@ class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
TwoGangGlassStatusModel deviceStatus;
Timer? _timer;
TwoGangGlassSwitchBloc({required String deviceId})
: deviceStatus = TwoGangGlassStatusModel(
uuid: deviceId,
@ -28,6 +26,7 @@ class TwoGangGlassSwitchBloc
on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<TwoGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event,
@ -38,12 +37,44 @@ class TwoGangGlassSwitchBloc
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangGlassSwitchError(e.toString()));
}
}
void _listenToChanges(String deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
ref.onValue.listen((DatabaseEvent event) {
if (event.snapshot.value == null) return;
Map<dynamic, dynamic> data =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
data['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
// Parse the new status and add the event
final updatedStatus =
TwoGangGlassStatusModel.fromJson(data['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(updatedStatus));
}
});
} catch (e) {
// Handle errors and emit an error state if necessary
if (!isClosed) {
// add(TwoGangGlassSwitchError('Error listening to updates: $e'));
}
}
}
Future<void> _onControl(TwoGangGlassSwitchControl event,
Emitter<TwoGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code);
@ -178,4 +209,37 @@ class TwoGangGlassSwitchBloc
_timer?.cancel();
return super.close();
}
// _listenToChanges(deviceId) {
// try {
// DatabaseReference ref =
// FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap =
// event.snapshot.value as Map<dynamic, dynamic>;
// List<Status> statusList = [];
// usersMap['status'].forEach((element) {
// statusList
// .add(Status(code: element['code'], value: element['value']));
// });
// deviceStatus = TwoGangGlassStatusModel.fromJson(
// usersMap['productUuid'], statusList);
// if (!isClosed) {
// add(StatusUpdated(deviceStatus));
// }
// });
// } catch (_) {}
// }
void _onStatusUpdated(
StatusUpdated event, Emitter<TwoGangGlassSwitchState> emit) {
// Update the local deviceStatus with the new status from the event
deviceStatus = event.deviceStatus;
// Emit the new state with the updated status
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
}
}

View File

@ -48,3 +48,11 @@ class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent {
required this.factoryReset,
});
}
class StatusUpdated extends TwoGangGlassSwitchEvent {
final TwoGangGlassStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -6,7 +6,8 @@ import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout {
class TwoGangGlassSwitchControlView extends StatelessWidget
with HelperResponsiveLayout {
final String deviceId;
const TwoGangGlassSwitchControlView({required this.deviceId, super.key});
@ -14,25 +15,25 @@ class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) =>
TwoGangGlassSwitchBloc(deviceId: deviceId)..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) {
if (state is TwoGangGlassSwitchLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is TwoGangGlassSwitchStatusLoaded) {
return _buildStatusControls(context, state.status);
} else if (state is TwoGangGlassSwitchError) {
return const Center(child: Text('Error fetching status'));
} else {
return const Center(child: CircularProgressIndicator());
}
},
),
);
create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId)
..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) {
if (state is TwoGangGlassSwitchLoading) {
return const Center(child: CircularProgressIndicator());
} else if (state is TwoGangGlassSwitchStatusLoaded) {
return _buildStatusControls(context, state.status);
} else if (state is TwoGangGlassSwitchError) {
return Center(child: Text(state.message));
} else {
return const Center(child: CircularProgressIndicator());
}
},
));
}
Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel status) {
Widget _buildStatusControls(
BuildContext context, TwoGangGlassStatusModel status) {
final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context);

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
@ -14,6 +15,7 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<TwoGangSwitchBatchControl>(_onBatchControl);
on<TwoGangFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
late TwoGangStatusModel deviceStatus;
@ -26,8 +28,8 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
try {
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangSwitchStatusLoaded(deviceStatus));
} catch (e) {
emit(TwoGangSwitchError(e.toString()));
@ -174,4 +176,34 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
emit(TwoGangSwitchError(e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
TwoGangStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<TwoGangSwitchState> emit) {
deviceStatus = event.deviceStatus;
emit(TwoGangSwitchStatusLoaded(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/two_gang_switch/models/two_gang_status_model.dart';
class TwoGangSwitchEvent extends Equatable {
@override
@ -57,3 +58,10 @@ class TwoGangFactoryReset extends TwoGangSwitchEvent {
@override
List<Object> get props => [deviceId, factoryReset];
}
class StatusUpdated extends TwoGangSwitchEvent {
final TwoGangStatusModel deviceStatus;
StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
@ -29,7 +30,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
// _listenToChanges();
_listenToChanges(emit, deviceId);
} catch (e) {
emit(WallSensorFailedState(error: e.toString()));
return;
@ -38,10 +39,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
// Fetch batch status
FutureOr<void> _fetchWallSensorBatchControl(
WallSensorFetchBatchStatusEvent event, Emitter<WallSensorState> emit) async {
WallSensorFetchBatchStatusEvent event,
Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingInitialState());
try {
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} catch (e) {
@ -49,26 +52,30 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
}
}
// _listenToChanges() {
// try {
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
// Stream<DatabaseEvent> stream = ref.onValue;
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>;
// List<StatusModel> statusList = [];
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
// usersMap['status'].forEach((element) {
// statusList.add(StatusModel(code: element['code'], value: element['value']));
// });
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
// deviceStatus = WallSensorModel.fromJson(statusList);
// add(WallSensorUpdatedEvent());
// });
// } catch (_) {}
// }
deviceStatus = WallSensorModel.fromJson(statusList);
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
});
} catch (_) {}
}
void _changeValue(WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
void _changeValue(
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
if (event.code == 'far_detection') {
deviceStatus.farDetection = event.value;
@ -125,7 +132,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try {
late bool response;
if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else {
response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
@ -150,7 +158,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try {
// await DevicesManagementApi.getDeviceReportsByDate(
// deviceId, event.code, from.toString(), to.toString())
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) {
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
.then((value) {
emit(DeviceReportsState(deviceReport: value, code: event.code));
});
} catch (e) {
@ -159,11 +168,13 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
}
}
void _showDescription(ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
void _showDescription(
ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
emit(WallSensorShowDescriptionState(description: event.description));
}
void _backToGridView(BackToGridViewEvent event, Emitter<WallSensorState> emit) {
void _backToGridView(
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
}

View File

@ -4,6 +4,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
@ -34,6 +35,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
on<EditWaterHeaterScheduleEvent>(_onEditSchedule);
on<DeleteScheduleEvent>(_onDeleteSchedule);
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
on<StatusUpdated>(_onStatusUpdated);
}
late WaterHeaterStatusModel deviceStatus;
@ -78,7 +80,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
final currentState = state as WaterHeaterDeviceStatusLoaded;
final updatedDays = List<bool>.from(currentState.selectedDays);
updatedDays[event.index] = event.value;
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime));
emit(currentState.copyWith(
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
}
FutureOr<void> _updateFunctionOn(
@ -86,7 +89,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
Emitter<WaterHeaterState> emit,
) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
emit(currentState.copyWith(functionOn: event.isOn, selectedTime: currentState.selectedTime));
emit(currentState.copyWith(
functionOn: event.isOn, selectedTime: currentState.selectedTime));
}
FutureOr<void> _updateScheduleEvent(
@ -101,7 +105,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
));
}
if (event.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration(hours: event.hours, minutes: event.minutes);
final countdownRemaining =
Duration(hours: event.hours, minutes: event.minutes);
emit(currentState.copyWith(
scheduleMode: ScheduleModes.countdown,
@ -111,11 +116,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
countdownRemaining: countdownRemaining,
));
if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) {
if (!currentState.isCountdownActive! &&
countdownRemaining > Duration.zero) {
_startCountdownTimer(emit, countdownRemaining);
}
} else if (event.scheduleMode == ScheduleModes.inching) {
final inchingDuration = Duration(hours: event.hours, minutes: event.minutes);
final inchingDuration =
Duration(hours: event.hours, minutes: event.minutes);
emit(currentState.copyWith(
scheduleMode: ScheduleModes.inching,
@ -217,7 +224,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
try {
final status = await DevicesManagementApi().deviceControl(
event.deviceId,
Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
Status(
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
);
if (!status) {
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
@ -235,8 +243,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(WaterHeaterLoadingState());
try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration(
@ -300,11 +310,42 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
isInchingActive: false,
));
}
_listenToChanges(event.deviceId);
} catch (e) {
emit(WaterHeaterFailedState(error: e.toString()));
}
}
_listenToChanges(deviceId) {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus = WaterHeaterStatusModel.fromJson(
usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<WaterHeaterState> emit) {
deviceStatus = event.deviceStatus;
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
}
void _startCountdownTimer(
Emitter<WaterHeaterState> emit,
Duration countdownRemaining,
@ -334,8 +375,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
if (currentState.countdownRemaining != null && currentState.countdownRemaining! > Duration.zero) {
final newRemaining = currentState.countdownRemaining! - const Duration(minutes: 1);
if (currentState.countdownRemaining != null &&
currentState.countdownRemaining! > Duration.zero) {
final newRemaining =
currentState.countdownRemaining! - const Duration(minutes: 1);
if (newRemaining <= Duration.zero) {
_countdownTimer?.cancel();
@ -430,7 +473,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}
}
void _revertValue(String code, dynamic oldValue, void Function(WaterHeaterState state) emit) {
void _revertValue(String code, dynamic oldValue,
void Function(WaterHeaterState state) emit) {
_updateLocalValue(code, oldValue);
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -477,12 +521,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
return super.close();
}
FutureOr<void> _getSchedule(GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
FutureOr<void> _getSchedule(
GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
emit(ScheduleLoadingState());
try {
List<ScheduleModel> schedules =
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category);
List<ScheduleModel> schedules = await DevicesManagementApi()
.getDeviceSchedules(deviceStatus.uuid, event.category);
emit(WaterHeaterDeviceStatusLoaded(
deviceStatus,
@ -514,7 +559,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, currentState.status.uuid);
bool success = await DevicesManagementApi()
.addScheduleRecord(newSchedule, currentState.status.uuid);
if (success) {
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid));
@ -525,7 +571,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}
}
FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event, Emitter<WaterHeaterState> emit) async {
FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event,
Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -594,11 +641,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
bool success = await DevicesManagementApi()
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
if (success) {
final updatedSchedules =
currentState.schedules.where((schedule) => schedule.scheduleId != event.scheduleId).toList();
final updatedSchedules = currentState.schedules
.where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
emit(currentState.copyWith(schedules: updatedSchedules));
} else {
@ -608,12 +657,15 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}
}
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event, Emitter<WaterHeaterState> emit) async {
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event,
Emitter<WaterHeaterState> emit) async {
emit(WaterHeaterLoadingState());
try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesUuid);
deviceStatus = WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status);
final status =
await DevicesManagementApi().getBatchStatus(event.devicesUuid);
deviceStatus = WaterHeaterStatusModel.fromJson(
event.devicesUuid.first, status.status);
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} catch (e) {
@ -621,7 +673,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
}
}
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event, Emitter<WaterHeaterState> emit) async {
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event,
Emitter<WaterHeaterState> emit) async {
if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded;

View File

@ -54,6 +54,15 @@ final class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
final class DecrementCountdownEvent extends WaterHeaterEvent {}
class StatusUpdated extends WaterHeaterEvent {
final WaterHeaterStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}
final class AddScheduleEvent extends WaterHeaterEvent {
final List<bool> selectedDays;
final TimeOfDay time;

View File

@ -95,6 +95,77 @@ class CountdownInchingView extends StatelessWidget {
);
}
Row _hourMinutesSecondWheel(
BuildContext context, WaterHeaterDeviceStatusLoaded state) {
final isCountDown =
state.scheduleMode?.name == ScheduleModes.countdown.name;
late bool isActive;
if (isCountDown &&
state.countdownRemaining != null &&
state.isCountdownActive == true) {
isActive = true;
} else if (!isCountDown &&
state.countdownRemaining != null &&
state.isInchingActive == true) {
isActive = true;
} else {
isActive = false;
}
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildPickerColumn(
context,
'h',
isCountDown
? (state.countdownHours ?? 0)
: (state.inchingHours ?? 0),
24, (value) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
hours: value,
minutes: isCountDown
? (state.countdownMinutes ?? 0)
: (state.inchingMinutes ?? 0),
));
}, isActive: isActive),
const SizedBox(width: 10),
_buildPickerColumn(
context,
'm',
isCountDown
? (state.countdownMinutes ?? 0)
: (state.inchingMinutes ?? 0),
60, (value) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
hours: isCountDown
? (state.countdownHours ?? 0)
: (state.inchingHours ?? 0),
minutes: value,
));
}, isActive: isActive),
const SizedBox(width: 10),
_buildPickerColumn(
context,
'S',
isCountDown
? (state.countdownMinutes ?? 0)
: (state.inchingMinutes ?? 0),
60, (value) {
context.read<WaterHeaterBloc>().add(UpdateScheduleEvent(
scheduleMode: state.scheduleMode ?? ScheduleModes.countdown,
hours: isCountDown
? (state.countdownHours ?? 0)
: (state.inchingHours ?? 0),
minutes: value,
));
}, isActive: isActive),
],
);
}
Widget _buildPickerColumn(
BuildContext context,
String label,

View File

@ -1,4 +1,5 @@
import 'package:bloc/bloc.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/water_leak/bloc/water_leak_event.dart';
@ -21,6 +22,7 @@ class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
on<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus);
on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports);
on<WaterLeakFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
}
Future<void> _onFetchWaterLeakStatus(
@ -30,12 +32,43 @@ class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
final response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
_listenToChanges();
emit(WaterLeakLoadedState(deviceStatus!));
} catch (e) {
emit(WaterLeakErrorState(e.toString()));
}
}
_listenToChanges() {
try {
DatabaseReference ref =
FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
stream.listen((DatabaseEvent event) {
Map<dynamic, dynamic> usersMap =
event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
usersMap['status'].forEach((element) {
statusList
.add(Status(code: element['code'], value: element['value']));
});
deviceStatus =
WaterLeakStatusModel.fromJson(usersMap['productUuid'], statusList);
if (!isClosed) {
add(StatusUpdated(deviceStatus!));
}
});
} catch (_) {}
}
void _onStatusUpdated(StatusUpdated event, Emitter<WaterLeakState> emit) {
deviceStatus = event.deviceStatus;
emit(WaterLeakLoadedState(deviceStatus!));
}
Future<void> _onControl(
WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async {
final oldValue = deviceStatus!.watersensorState;

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/water_leak/model/water_leak_status_model.dart';
abstract class WaterLeakEvent extends Equatable {
const WaterLeakEvent();
@ -17,6 +18,13 @@ class FetchWaterLeakStatusEvent extends WaterLeakEvent {
List<Object> get props => [deviceId];
}
class StatusUpdated extends WaterLeakEvent {
final WaterLeakStatusModel deviceStatus;
const StatusUpdated(this.deviceStatus);
@override
List<Object> get props => [deviceStatus];
}
class WaterLeakControlEvent extends WaterLeakEvent {
final String deviceId;
final String code;

View File

@ -2,17 +2,19 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.dart';
// import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/auth/model/user_model.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/home_model/home_item_model.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/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
// final Graph graph = Graph()..isTree = true;
@ -22,9 +24,8 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
UserModel? user;
String terms = '';
String policy = '';
final ProjectCubit projectCubit;
HomeBloc(this.projectCubit) : super((HomeInitial())) {
HomeBloc() : super((HomeInitial())) {
// on<CreateNewNode>(_createNode);
on<FetchUserInfo>(_fetchUserInfo);
on<FetchTermEvent>(_fetchTerms);
@ -50,12 +51,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try {
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid);
if (user != null && user!.project != null) {
projectCubit.setProjectUUID(user!.project!.uuid);
await ProjectManager.setProjectUUID(user!.project!.uuid);
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
}
add(FetchTermEvent());
add(FetchPolicyEvent());
@ -71,8 +72,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
emit(LoadingHome());
terms = await HomeApi().fetchTerms();
emit(HomeInitial());
// emit(PolicyAgreement());
} catch (e) {
return;
}
@ -82,8 +81,6 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
try {
emit(LoadingHome());
policy = await HomeApi().fetchPolicy();
debugPrint("Fetched policy: $policy");
// Emit a state to trigger the UI update
emit(HomeInitial());
} catch (e) {
debugPrint("Error fetching policy: $e");
@ -91,12 +88,10 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
}
}
Future _confirmUserAgreement(
ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
try {
emit(LoadingHome());
var uuid =
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
policy = await HomeApi().confirmUserAgreements(uuid);
emit(PolicyAgreement());
} catch (e) {
@ -116,10 +111,11 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
List<HomeItemModel> homeItems = [
HomeItemModel(
title: 'Access',
title: 'Access Management',
icon: Assets.accessIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.accessManagementPage);
},
color: null,
@ -129,21 +125,24 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.spaseManagementIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.spacesManagementPage);
},
color: ColorsManager.primaryColor,
),
HomeItemModel(
title: 'Devices',
title: 'Devices Management',
icon: Assets.devicesIcon,
active: true,
onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
BlocProvider.of<RoutineBloc>(context)
.add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.go(RoutesConst.deviceManagementPage);
},
color: ColorsManager.primaryColor,
),
// HomeItemModel(
// title: 'Move in',
// icon: Assets.moveinIcon,

View File

@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:go_router/go_router.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:url_launcher/url_launcher.dart';
@ -164,8 +164,7 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
children: [
InkWell(
onTap: () {
final projectCubit = BlocProvider.of<ProjectCubit>(context);
AuthBloc.logout(context, projectCubit);
AuthBloc.logout(context);
context.go(RoutesConst.auth);
},
child: const Text("Cancel"),

View File

@ -219,6 +219,9 @@ class UserSpaceModel {
final double x;
final double y;
final String icon;
final String communityUuid;
//communityUuid
UserSpaceModel({
required this.uuid,
@ -231,22 +234,23 @@ class UserSpaceModel {
required this.x,
required this.y,
required this.icon,
required this.communityUuid,
});
/// Create a [UserSpaceModel] from JSON data
factory UserSpaceModel.fromJson(Map<String, dynamic> json) {
return UserSpaceModel(
uuid: json['uuid'] as String,
createdAt: json['createdAt'] as String,
updatedAt: json['updatedAt'] as String,
spaceTuyaUuid: json['spaceTuyaUuid'] as String?,
spaceName: json['spaceName'] as String,
invitationCode: json['invitationCode'] as String?,
disabled: json['disabled'] as bool,
x: (json['x'] as num).toDouble(),
y: (json['y'] as num).toDouble(),
icon: json['icon'] as String,
);
uuid: json['uuid'] as String,
createdAt: json['createdAt'] as String,
updatedAt: json['updatedAt'] as String,
spaceTuyaUuid: json['spaceTuyaUuid'] as String?,
spaceName: json['spaceName'] as String,
invitationCode: json['invitationCode'] as String?,
disabled: json['disabled'] as bool,
x: (json['x'] as num).toDouble(),
y: (json['y'] as num).toDouble(),
icon: json['icon'] as String,
communityUuid: json['communityUuid'] as String);
}
/// Convert the [UserSpaceModel] to JSON
@ -262,6 +266,7 @@ class UserSpaceModel {
'x': x,
'y': y,
'icon': icon,
'communityUuid': communityUuid
};
}
}

View File

@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/custom_dialog.dart';
import 'package:syncrow_web/pages/roles_and_permission/model/edit_user_model.dart';
import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.dart';
@ -8,17 +9,17 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialo
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.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/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart';
import 'package:syncrow_web/services/user_permission.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class UsersBloc extends Bloc<UsersEvent, UsersState> {
final ProjectCubit projectCubit;
UsersBloc(this.projectCubit) : super(UsersInitial()) {
UsersBloc() : super(UsersInitial()) {
on<CheckStepStatus>(isCompleteBasicsFun);
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
on<SearchAnode>(searchTreeNode);
@ -65,8 +66,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
void isCompleteSpacesFun(
CheckSpacesStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities);
isCompleteSpaces = selectedIds.isNotEmpty;
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
isCompleteSpaces = spaceBloc.state.selectedCommunities.isNotEmpty;
emit(ChangeStatusSteps());
}
@ -78,10 +81,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
Future<List<SpaceModel>> _fetchSpacesForCommunity(
String communityUuid) async {
final projectUuid = projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
return await CommunitySpaceManagementApi()
.getSpaceHierarchy(communityUuid, projectUuid ?? TempConst.projectId);
.getSpaceHierarchy(communityUuid, projectUuid);
}
List<TreeNode> updatedCommunities = [];
@ -91,9 +94,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async {
try {
emit(UsersLoadingState());
final projectUuid = projectCubit.state;
List<CommunityModel> communities = await CommunitySpaceManagementApi()
.fetchCommunities(projectUuid ?? TempConst.projectId);
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
List<CommunityModel> communities =
await CommunitySpaceManagementApi().fetchCommunities(projectUuid);
communityIds = communities.map((community) => community.uuid).toList();
updatedCommunities = await Future.wait(
communities.map((community) async {
@ -336,12 +340,14 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
void _sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
try {
final projectUuid = projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities)
.where((id) => !communityIds.contains(id))
.toList();
// List<String> selectedIds =
// getSelectedIds(updatedCommunities).where((id) => !communityIds.contains(id)).toList();
List<String> selectedSpacesId = getSelectedSpacesIds();
// List<String> selectedIds = getSelectedIds(updatedCommunities);
bool res = await UserPermissionApi().sendInviteUser(
email: emailController.text,
@ -350,8 +356,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: roleSelected,
spaceUuids: selectedIds,
projectUuid: projectUuid ?? TempConst.projectId);
spaceUuids: selectedSpacesId,
projectUuid: projectUuid);
if (res) {
showCustomDialog(
@ -380,13 +386,21 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
}
}
List<String> getSelectedSpacesIds() {
List<String> selectedSpacesId = [];
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
for (var community in spaceBloc.state.selectedCommunities) {
selectedSpacesId
.addAll(spaceBloc.state.selectedCommunityAndSpaces[community] ?? []);
}
return selectedSpacesId;
}
_editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
try {
emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities)
.where((id) => !communityIds.contains(id))
.toList();
final projectUuid = projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
bool res = await UserPermissionApi().editInviteUser(
userId: event.userId,
@ -395,8 +409,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: roleSelected,
spaceUuids: selectedIds,
projectUuid: projectUuid ?? TempConst.projectId);
spaceUuids: getSelectedSpacesIds(),
projectUuid: projectUuid);
if (res == true) {
showCustomDialog(
barrierDismissible: false,
@ -501,11 +515,13 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
emit(UsersLoadingState());
try {
final projectUuid = projectCubit.state;
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (event.uuid?.isNotEmpty ?? false) {
final res = await UserPermissionApi()
.fetchUserById(event.uuid, projectUuid ?? TempConst.projectId);
final res =
await UserPermissionApi().fetchUserById(event.uuid, projectUuid);
if (res != null) {
// Populate the text controllers
@ -515,13 +531,20 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
phoneController.text = res.phoneNumber ?? '';
jobTitleController.text = res.jobTitle ?? '';
res.roleType;
if (updatedCommunities.isNotEmpty) {
// Create a list of UUIDs to mark
final uuidsToMark = res.spaces.map((space) => space.uuid).toList();
// Print all IDs and mark nodes in updatedCommunities
debugPrint('Printing and marking nodes in updatedCommunities:');
_printAndMarkNodes(updatedCommunities, uuidsToMark);
}
res.spaces.map((space) {
selectedIds.add(space.uuid);
CommunityModel community = spaceBloc.state.communityList
.firstWhere((item) => item.uuid == space.communityUuid);
spaceBloc.add(OnSpaceSelected(community, space.uuid, []));
}).toList();
// if (updatedCommunities.isNotEmpty) {
// // Create a list of UUIDs to mark
// final uuidsToMark = res.spaces.map((space) => space.uuid).toList();
// // Print all IDs and mark nodes in updatedCommunities
// debugPrint('Printing and marking nodes in updatedCommunities:');
// _printAndMarkNodes(updatedCommunities, uuidsToMark);
// }
final roleId = roles
.firstWhere((element) =>
element.type ==
@ -614,4 +637,16 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
}
return null;
}
@override
Future<void> close() {
emailController.dispose();
firstNameController.dispose();
lastNameController.dispose();
emailController.dispose();
phoneController.dispose();
jobTitleController.dispose();
roleSearchController.dispose();
return super.close();
}
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
@ -24,8 +23,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => UsersBloc(context.read<ProjectCubit>())
..add(const LoadCommunityAndSpacesEvent())
create: (BuildContext context) => UsersBloc()
// ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()),
child: BlocConsumer<UsersBloc, UsersState>(
listener: (context, state) {},

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
@ -20,9 +20,8 @@ class TreeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final _blocRole = BlocProvider.of<UsersBloc>(context);
debugPrint('TreeView constructed with userId = $userId');
return BlocProvider(
create: (_) => UsersBloc(context.read<ProjectCubit>()),
create: (_) => UsersBloc(),
// ..add(const LoadCommunityAndSpacesEvent()),
child: BlocConsumer<UsersBloc, UsersState>(
listener: (context, state) {

View File

@ -1,7 +1,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/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
@ -25,14 +25,13 @@ class _EditUserDialogState extends State<EditUserDialog> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) => UsersBloc(context.read<ProjectCubit>())
..add(const LoadCommunityAndSpacesEvent())
create: (BuildContext context) => UsersBloc()
// ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent())
..add(GetUserByIdEvent(uuid: widget.userId)),
child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) {
if (state is SpacesLoadedState) {
BlocProvider.of<UsersBloc>(context)
.add(GetUserByIdEvent(uuid: widget.userId));
BlocProvider.of<UsersBloc>(context).add(GetUserByIdEvent(uuid: widget.userId));
}
}, builder: (context, state) {
final _blocRole = BlocProvider.of<UsersBloc>(context);
@ -40,8 +39,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
return Dialog(
child: Container(
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.all(Radius.circular(20))),
color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
width: 900,
child: Column(
children: [
@ -70,8 +68,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
children: [
_buildStep1Indicator(1, "Basics", _blocRole),
_buildStep2Indicator(2, "Spaces", _blocRole),
_buildStep3Indicator(
3, "Role & Permissions", _blocRole),
_buildStep3Indicator(3, "Role & Permissions", _blocRole),
],
),
),
@ -119,15 +116,13 @@ class _EditUserDialogState extends State<EditUserDialog> {
if (currentStep < 3) {
currentStep++;
if (currentStep == 2) {
_blocRole
.add(CheckStepStatus(isEditUser: true));
_blocRole.add(CheckStepStatus(isEditUser: true));
} else if (currentStep == 3) {
_blocRole.add(const CheckSpacesStepStatus());
}
} else {
_blocRole.add(EditInviteUsers(
context: context,
userId: widget.userId!));
_blocRole
.add(EditInviteUsers(context: context, userId: widget.userId!));
}
});
},
@ -136,8 +131,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
style: TextStyle(
color: (_blocRole.isCompleteSpaces == false ||
_blocRole.isCompleteBasics == false ||
_blocRole.isCompleteRolePermissions ==
false) &&
_blocRole.isCompleteRolePermissions == false) &&
currentStep == 3
? ColorsManager.grayColor
: ColorsManager.secondaryColor),
@ -210,12 +204,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label,
style: TextStyle(
fontSize: 16,
color: currentStep == step
? ColorsManager.blackColor
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
),
),
],
@ -273,12 +263,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label,
style: TextStyle(
fontSize: 16,
color: currentStep == step
? ColorsManager.blackColor
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
),
),
],
@ -335,12 +321,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label,
style: TextStyle(
fontSize: 16,
color: currentStep == step
? ColorsManager.blackColor
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
),
),
],

View File

@ -1,14 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/style.dart';
class SpacesAccessView extends StatelessWidget {
final String? userId;
@ -27,10 +22,8 @@ class SpacesAccessView extends StatelessWidget {
children: [
Text(
'Spaces access',
style: context.textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.w700,
fontSize: 20,
color: Colors.black),
style: context.textTheme.bodyLarge
?.copyWith(fontWeight: FontWeight.w700, fontSize: 20, color: Colors.black),
),
const SizedBox(
height: 35,
@ -42,77 +35,78 @@ class SpacesAccessView extends StatelessWidget {
const SizedBox(
height: 25,
),
Expanded(
child: SizedBox(
child: Column(
children: [
Expanded(
flex: 2,
child: Container(
decoration: const BoxDecoration(
color: ColorsManager.circleRolesBackground,
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20)),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(20)),
border: Border.all(
color: ColorsManager.grayBorder)),
child: TextFormField(
style:
const TextStyle(color: Colors.black),
// controller: _blocRole.firstNameController,
onChanged: (value) {
_blocRole.add(SearchAnode(
nodes: _blocRole.updatedCommunities,
searchTerm: value));
},
decoration: textBoxDecoration(radios: 20)!
.copyWith(
fillColor: Colors.white,
suffixIcon: Padding(
padding:
const EdgeInsets.only(right: 16),
child: SvgPicture.asset(
Assets.textFieldSearch,
width: 24,
height: 24,
),
),
hintStyle: context.textTheme.bodyMedium
?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
color: ColorsManager.textGray),
),
),
),
),
],
),
),
),
),
Expanded(
flex: 7,
child: Container(
color: ColorsManager.circleRolesBackground,
padding: const EdgeInsets.all(8.0),
child: Container(
color: ColorsManager.whiteColors,
child: TreeView(userId: userId))))
],
),
),
),
Expanded(child: SpaceTreeView(onSelect: () {}))
// Expanded(
// child: SizedBox(
// child: Column(
// children: [
// Expanded(
// flex: 2,
// child: Container(
// decoration: const BoxDecoration(
// color: ColorsManager.circleRolesBackground,
// borderRadius: BorderRadius.only(
// topRight: Radius.circular(20),
// topLeft: Radius.circular(20)),
// ),
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// children: [
// Expanded(
// child: Container(
// decoration: BoxDecoration(
// borderRadius: const BorderRadius.all(
// Radius.circular(20)),
// border: Border.all(
// color: ColorsManager.grayBorder)),
// child: TextFormField(
// style:
// const TextStyle(color: Colors.black),
// // controller: _blocRole.firstNameController,
// onChanged: (value) {
// _blocRole.add(SearchAnode(
// nodes: _blocRole.updatedCommunities,
// searchTerm: value));
// },
// decoration: textBoxDecoration(radios: 20)!
// .copyWith(
// fillColor: Colors.white,
// suffixIcon: Padding(
// padding:
// const EdgeInsets.only(right: 16),
// child: SvgPicture.asset(
// Assets.textFieldSearch,
// width: 24,
// height: 24,
// ),
// ),
// hintStyle: context.textTheme.bodyMedium
// ?.copyWith(
// fontWeight: FontWeight.w400,
// fontSize: 12,
// color: ColorsManager.textGray),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// ),
// ),
// Expanded(
// flex: 7,
// child: Container(
// color: ColorsManager.circleRolesBackground,
// padding: const EdgeInsets.all(8.0),
// child: Container(
// color: ColorsManager.whiteColors,
// child: TreeView(userId: userId))))
// ],
// ),
// ),
// ),
],
),
),

View File

@ -1,17 +1,16 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart';
import 'package:syncrow_web/services/user_permission.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
final ProjectCubit _projectCubit;
UserTableBloc(this._projectCubit) : super(TableInitial()) {
UserTableBloc() : super(TableInitial()) {
on<GetUsers>(_getUsers);
on<ChangeUserStatus>(_changeUserStatus);
on<SortUsersByNameAsc>(_toggleSortUsersByNameAsc);
@ -50,13 +49,12 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async {
emit(UsersLoadingState());
try {
final projectUuid = _projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
roleTypes.clear();
jobTitle.clear();
createdBy.clear();
users = await UserPermissionApi()
.fetchUsers(projectUuid ?? TempConst.projectId);
users = await UserPermissionApi().fetchUsers(projectUuid);
users.sort((a, b) {
final dateA = _parseDateTime(a.createdDate);
final dateB = _parseDateTime(b.createdDate);
@ -103,13 +101,11 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
Future<void> _changeUserStatus(
ChangeUserStatus event, Emitter<UserTableState> emit) async {
try {
final projectUuid = _projectCubit.state;
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(UsersLoadingState());
bool res = await UserPermissionApi().changeUserStatusById(
event.userId,
event.newStatus == "disabled" ? false : true,
projectUuid ?? TempConst.projectId);
bool res = await UserPermissionApi().changeUserStatusById(event.userId,
event.newStatus == "disabled" ? false : true, projectUuid);
if (res == true) {
add(const GetUsers());
}

View File

@ -13,6 +13,8 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/vi
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/name_filter.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/user_table.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/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -25,8 +27,7 @@ class UsersPage extends StatelessWidget {
Widget build(BuildContext context) {
final TextEditingController searchController = TextEditingController();
Widget actionButton(
{bool isActive = false, required String title, Function()? onTap}) {
Widget actionButton({bool isActive = false, required String title, Function()? onTap}) {
return InkWell(
onTap: onTap,
child: Padding(
@ -59,8 +60,7 @@ class UsersPage extends StatelessWidget {
: ColorsManager.disabledPink.withOpacity(0.5),
),
child: Padding(
padding:
const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
@ -84,15 +84,12 @@ class UsersPage extends StatelessWidget {
}
Widget changeIconStatus(
{required String userId,
required String status,
required Function()? onTap}) {
{required String userId, required String status, required Function()? onTap}) {
return Center(
child: InkWell(
onTap: onTap,
child: Padding(
padding:
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
child: SvgPicture.asset(
status == "invited"
? Assets.invitedIcon
@ -160,6 +157,7 @@ class UsersPage extends StatelessWidget {
const SizedBox(width: 20),
InkWell(
onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog(
context: context,
barrierDismissible: false,
@ -198,14 +196,10 @@ class UsersPage extends StatelessWidget {
context: context,
isSelected: _blocRole.currentSortOrder,
aToZTap: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
context.read<UserTableBloc>().add(const SortUsersByNameAsc());
},
zToaTap: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
context.read<UserTableBloc>().add(const SortUsersByNameDesc());
},
);
}
@ -214,9 +208,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.jobTitle)
item: _blocRole.selectedJobTitles.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
showPopUpFilterMenu(
position: RelativeRect.fromLTRB(
@ -256,9 +249,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.roleTypes)
item: _blocRole.selectedRoles.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
showPopUpFilterMenu(
position: RelativeRect.fromLTRB(
overlay.size.width / 4,
@ -278,10 +270,9 @@ class UsersPage extends StatelessWidget {
.map((entry) => entry.key)
.toList();
Navigator.of(context).pop();
context.read<UserTableBloc>().add(
FilterUsersByRoleEvent(
selectedRoles: selectedItems,
sortOrder: _blocRole.currentSortRole));
context.read<UserTableBloc>().add(FilterUsersByRoleEvent(
selectedRoles: selectedItems,
sortOrder: _blocRole.currentSortRole));
},
onSortAtoZ: (v) {
_blocRole.currentSortRole = v;
@ -296,14 +287,10 @@ class UsersPage extends StatelessWidget {
context: context,
isSelected: _blocRole.currentSortOrder,
aToZTap: () {
context
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
},
zToaTap: () {
context
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
},
);
}
@ -312,9 +299,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.createdBy)
item: _blocRole.selectedCreatedBy.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
showPopUpFilterMenu(
position: RelativeRect.fromLTRB(
overlay.size.width / 1,
@ -352,9 +338,8 @@ class UsersPage extends StatelessWidget {
item: _blocRole.selectedStatuses.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
showPopUpFilterMenu(
position: RelativeRect.fromLTRB(
overlay.size.width / 0,
@ -391,14 +376,10 @@ class UsersPage extends StatelessWidget {
context: context,
isSelected: _blocRole.currentSortOrderDate,
aToZTap: () {
context
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
},
zToaTap: () {
context
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
},
);
}
@ -425,23 +406,17 @@ class UsersPage extends StatelessWidget {
Text(user.createdTime ?? ''),
Text(user.invitedBy),
status(
status: user.isEnabled == false
? 'disabled'
: user.status,
status: user.isEnabled == false ? 'disabled' : user.status,
),
changeIconStatus(
status: user.isEnabled == false
? 'disabled'
: user.status,
status: user.isEnabled == false ? 'disabled' : user.status,
userId: user.uuid,
onTap: user.status != "invited"
? () {
context.read<UserTableBloc>().add(
ChangeUserStatus(
userId: user.uuid,
newStatus: user.isEnabled == false
? 'disabled'
: user.status));
context.read<UserTableBloc>().add(ChangeUserStatus(
userId: user.uuid,
newStatus:
user.isEnabled == false ? 'disabled' : user.status));
}
: null,
),
@ -452,12 +427,12 @@ class UsersPage extends StatelessWidget {
isActive: true,
title: "Edit",
onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return EditUserDialog(
userId: user.uuid);
return EditUserDialog(userId: user.uuid);
},
).then((v) {
if (v != null) {
@ -478,13 +453,10 @@ class UsersPage extends StatelessWidget {
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return DeleteUserDialog(
onTapDelete: () async {
return DeleteUserDialog(onTapDelete: () async {
try {
_blocRole.add(DeleteUserEvent(
user.uuid, context));
await Future.delayed(
const Duration(seconds: 2));
_blocRole.add(DeleteUserEvent(user.uuid, context));
await Future.delayed(const Duration(seconds: 2));
return true;
} catch (e) {
return false;
@ -514,20 +486,14 @@ class UsersPage extends StatelessWidget {
visiblePagesCount: 4,
buttonRadius: 10,
selectedButtonColor: ColorsManager.secondaryColor,
buttonUnSelectedBorderColor:
ColorsManager.grayBorder,
lastPageIcon:
const Icon(Icons.keyboard_double_arrow_right),
firstPageIcon:
const Icon(Icons.keyboard_double_arrow_left),
totalPages: (_blocRole.totalUsersCount.length /
_blocRole.itemsPerPage)
.ceil(),
buttonUnSelectedBorderColor: ColorsManager.grayBorder,
lastPageIcon: const Icon(Icons.keyboard_double_arrow_right),
firstPageIcon: const Icon(Icons.keyboard_double_arrow_left),
totalPages:
(_blocRole.totalUsersCount.length / _blocRole.itemsPerPage).ceil(),
currentPage: _blocRole.currentPage,
onPageChanged: (int pageNumber) {
context
.read<UserTableBloc>()
.add(ChangePage(pageNumber));
context.read<UserTableBloc>().add(ChangePage(pageNumber));
},
),
),

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_state.dart';
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bl
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/users_page.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/theme/responsive_text_theme.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart';
class RolesAndPermissionPage extends StatelessWidget {
@ -27,11 +28,10 @@ class RolesAndPermissionPage extends StatelessWidget {
? const Center(child: CircularProgressIndicator())
: WebScaffold(
enableMenuSidebar: false,
appBarTitle: FittedBox(
child: Text(
'Roles & Permissions',
style: Theme.of(context).textTheme.headlineLarge,
),
appBarTitle: Text(
'Roles & Permissions',
style:
ResponsiveTextTheme.of(context).deviceManagementTitle,
),
rightBody: const NavigateHomeGridView(),
centerBody: Row(
@ -77,7 +77,7 @@ class RolesAndPermissionPage extends StatelessWidget {
],
),
scaffoldBody: BlocProvider<UserTableBloc>(
create: (context) => UserTableBloc(context.read<ProjectCubit>())..add(const GetUsers()),
create: (context) => UserTableBloc()..add(const GetUsers()),
child: UsersPage(),
)
// _blocRole.tapSelect == false

View File

@ -0,0 +1,39 @@
import 'dart:convert';
class AutomationStatusUpdate {
final String spaceUuid;
final bool isEnable;
AutomationStatusUpdate({
required this.spaceUuid,
required this.isEnable,
});
factory AutomationStatusUpdate.fromRawJson(String str) =>
AutomationStatusUpdate.fromJson(json.decode(str));
String toRawJson() => json.encode(toJson());
factory AutomationStatusUpdate.fromJson(Map<String, dynamic> json) =>
AutomationStatusUpdate(
spaceUuid: json["spaceUuid"],
isEnable: json["isEnable"],
);
Map<String, dynamic> toJson() => {
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
factory AutomationStatusUpdate.fromMap(Map<String, dynamic> map) =>
AutomationStatusUpdate(
spaceUuid: map["spaceUuid"],
isEnable: map["isEnable"],
);
Map<String, dynamic> toMap() => {
"spaceUuid": spaceUuid,
"isEnable": isEnable,
};
}

View File

@ -0,0 +1,67 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.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_state.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/services/space_mana_api.dart';
class CreateRoutineBloc extends Bloc<CreateRoutineEvent, CreateRoutineState> {
CreateRoutineBloc() : super(const CreateRoutineInitial()) {
on<SpaceOnlyWithDevicesEvent>(_fetchSpaceOnlyWithDevices);
on<SaveCommunityIdAndSpaceIdEvent>(saveSpaceIdCommunityId);
on<ResetSelectedEvent>(resetSelected);
on<FetchCommunityEvent>(_fetchCommunity);
}
String selectedSpaceId = '';
String selectedCommunityId = '';
List<CommunityModel> communities = [];
List<SpaceModel> spacesOnlyWithDevices = [];
Future<void> _fetchSpaceOnlyWithDevices(
SpaceOnlyWithDevicesEvent event, Emitter<CreateRoutineState> emit) async {
emit(const SpaceWithDeviceLoadingState());
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
spacesOnlyWithDevices = await CommunitySpaceManagementApi()
.getSpaceOnlyWithDevices(
communityId: event.communityID, projectId: projectUuid);
emit(SpaceWithDeviceLoadedState(spacesOnlyWithDevices));
} catch (e) {
emit(SpaceTreeErrorState('Error loading spaces: $e'));
}
}
saveSpaceIdCommunityId(
SaveCommunityIdAndSpaceIdEvent event, Emitter<CreateRoutineState> emit) {
emit(const SpaceWithDeviceLoadingState());
selectedSpaceId = event.spaceID!;
selectedCommunityId = event.communityID!;
emit(const SelectedState());
}
resetSelected(ResetSelectedEvent event, Emitter<CreateRoutineState> emit) {
emit(const SpaceWithDeviceLoadingState());
selectedSpaceId = '';
selectedCommunityId = '';
emit(const ResetSelectedState());
}
Future<void> _fetchCommunity(
FetchCommunityEvent event, Emitter<CreateRoutineState> emit) async {
emit(const CommunitiesLoadingState());
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
communities =
await CommunitySpaceManagementApi().fetchCommunities(projectUuid);
emit(const CommunityLoadedState());
} catch (e) {
emit(SpaceTreeErrorState('Error loading communities $e'));
}
}
}

View File

@ -0,0 +1,51 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
abstract class CreateRoutineEvent extends Equatable {
const CreateRoutineEvent();
@override
List<Object?> get props => [];
}
class AddToIfContainer extends CreateRoutineEvent {
final SpaceModel spaceModel;
const AddToIfContainer(this.spaceModel);
@override
List<Object?> get props => [spaceModel];
}
class SpaceOnlyWithDevicesEvent extends CreateRoutineEvent {
final String communityID;
const SpaceOnlyWithDevicesEvent(this.communityID);
@override
List<Object> get props => [communityID];
}
class SaveCommunityIdAndSpaceIdEvent extends CreateRoutineEvent {
final String? communityID;
final String? spaceID;
const SaveCommunityIdAndSpaceIdEvent({this.communityID, this.spaceID});
@override
List<Object> get props => [communityID!, spaceID!];
}
class ResetSelectedEvent extends CreateRoutineEvent {
const ResetSelectedEvent();
@override
List<Object> get props => [];
}
class FetchCommunityEvent extends CreateRoutineEvent {
const FetchCommunityEvent();
@override
List<Object> get props => [];
}

View File

@ -0,0 +1,53 @@
import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
abstract class CreateRoutineState extends Equatable {
const CreateRoutineState();
@override
List<Object?> get props => [];
}
class CreateRoutineInitial extends CreateRoutineState {
const CreateRoutineInitial();
}
class SpaceWithDeviceLoadingState extends CreateRoutineState {
const SpaceWithDeviceLoadingState();
}
class SpaceWithDeviceLoadedState extends CreateRoutineState {
final List<SpaceModel> spaces;
const SpaceWithDeviceLoadedState(this.spaces);
@override
List<Object?> get props => [spaces];
}
class SpaceTreeErrorState extends CreateRoutineState {
final String errorMessage;
const SpaceTreeErrorState(this.errorMessage);
@override
List<Object?> get props => [errorMessage];
}
class SelectedState extends CreateRoutineState {
const SelectedState();
}
class ResetSelectedState extends CreateRoutineState {
const ResetSelectedState();
}
class CommunityLoadedState extends CreateRoutineState {
const CommunityLoadedState();
}
class CommunitiesLoadingState extends CreateRoutineState {
const CommunitiesLoadingState();
}

View File

@ -3,31 +3,33 @@ import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/common/bloc/project_cubit.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.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/automation_scene_trigger_bloc/automation_status_update.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routines/models/delay/delay_fucntions.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routines/models/routine_model.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/services/devices_mang_api.dart';
import 'package:syncrow_web/services/routines_api.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/snack_bar.dart';
import 'package:uuid/uuid.dart';
part 'routine_event.dart';
part 'routine_state.dart';
String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
// String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
// String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final ProjectCubit projectCubit;
RoutineBloc(this.projectCubit) : super(const RoutineState()) {
RoutineBloc() : super(const RoutineState()) {
on<AddToIfContainer>(_onAddToIfContainer);
on<AddToThenContainer>(_onAddToThenContainer);
on<LoadScenes>(_onLoadScenes);
@ -52,7 +54,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
on<TriggerSwitchTabsEvent>(_triggerSwitchTabsEvent);
on<CreateNewRoutineViewEvent>(_createNewRoutineViewEvent);
on<ResetErrorMessage>(_resetErrorMessage);
on<SceneTrigger>(_onSceneTrigger);
on<UpdateAutomationStatus>(_onUpdateAutomationStatus);
}
String selectedSpaceId = '';
String selectedCommunityId = '';
FutureOr<void> _triggerSwitchTabsEvent(
TriggerSwitchTabsEvent event,
@ -62,8 +68,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
routineTab: event.isRoutineTab, createRoutineView: false));
add(ResetRoutineState());
if (event.isRoutineTab) {
add(LoadScenes(spaceId, communityId));
add(LoadAutomation(spaceId));
add(const LoadScenes());
add(const LoadAutomation());
}
}
@ -169,18 +175,29 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
final projectUuid = projectCubit.state;
spaceId = event.spaceId;
communityId = event.communityId;
List<ScenesModel> scenes = [];
if (communityId.isNotEmpty && spaceId.isNotEmpty) {
scenes = await SceneApi.getScenes(event.spaceId, event.communityId, projectUuid ?? TempConst.projectId);
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
@ -191,19 +208,36 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: []));
scenes: scenes));
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> automations = [];
final projectId = await ProjectManager.getProjectUUID() ?? '';
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
try {
spaceId = event.spaceId;
List<ScenesModel> automations = [];
if (spaceId.isNotEmpty) {
automations = await SceneApi.getAutomation(event.spaceId);
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
automations.addAll(
await SceneApi.getAutomation(spaceId, communityId, projectId));
}
}
} else {
automations.addAll(await SceneApi.getAutomation(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectId));
}
emit(state.copyWith(
automations: automations,
@ -215,7 +249,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '',
loadScenesErrorMessage: '',
automations: []));
automations: automations));
}
}
@ -302,8 +336,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
});
}).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final createSceneModel = CreateSceneModel(
spaceUuid: spaceId,
spaceUuid: createRoutineBloc.selectedSpaceId,
iconId: state.selectedIcon ?? '',
showInDevice: true,
sceneName: state.routineName ?? '',
@ -314,8 +351,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createScene(createSceneModel);
if (result['success']) {
add(ResetRoutineState());
add(LoadScenes(spaceId, communityId));
add(LoadAutomation(spaceId));
add(const LoadScenes());
add(const LoadAutomation());
} else {
emit(state.copyWith(
isLoading: false,
@ -333,6 +370,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith(
errorMessage: 'Automation name is required',
@ -428,9 +466,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
);
});
}).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final createAutomationModel = CreateAutomationModel(
spaceUuid: spaceId,
spaceUuid: createRoutineBloc.selectedSpaceId,
automationName: state.routineName ?? '',
decisionExpr: state.selectedAutomationOperator,
effectiveTime: EffectiveTime(
@ -442,11 +482,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions,
);
final result = await SceneApi.createAutomation(createAutomationModel);
final result =
await SceneApi.createAutomation(createAutomationModel, projectUuid);
if (result['success']) {
add(ResetRoutineState());
add(LoadAutomation(spaceId));
add(LoadScenes(spaceId, communityId));
add(const LoadAutomation());
add(const LoadScenes());
} else {
emit(state.copyWith(
isLoading: false,
@ -820,19 +861,36 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isUpdate: false,
createRoutineView: false));
}
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) {
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
try {
emit(state.copyWith(isLoading: true));
if (state.isTabToRun) {
SceneApi.deleteScene(unitUuid: spaceId, sceneId: state.sceneId ?? '');
} else {
SceneApi.deleteAutomation(
unitUuid: spaceId, automationId: state.automationId ?? '');
}
final projectId = await ProjectManager.getProjectUUID() ?? '';
add(LoadScenes(spaceId, communityId));
add(LoadAutomation(spaceId));
emit(state.copyWith(isLoading: true));
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
if (state.isTabToRun) {
await SceneApi.deleteScene(
unitUuid: spaceBloc.state.selectedSpaces[0], sceneId: state.sceneId ?? '');
} else {
await SceneApi.deleteAutomation(
unitUuid: spaceBloc.state.selectedSpaces[0],
automationId: state.automationId ?? '',
projectId: projectId);
}
// var createRoutineBloc = context.read<CreateRoutineBloc>();
// if (state.isTabToRun) {
// await SceneApi.deleteScene(
// unitUuid: createRoutineBloc.selectedSpaceId,
// sceneId: state.sceneId ?? '');
// } else {
// await SceneApi.deleteAutomation(
// projectId: projectId,
// unitUuid: createRoutineBloc.selectedSpaceId,
// automationId: state.automationId ?? '');
// }
add(const LoadScenes());
add(const LoadAutomation());
add(ResetRoutineState());
emit(state.copyWith(isLoading: false, createRoutineView: false));
} catch (e) {
@ -842,7 +900,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
// FutureOr<void> _deleteAutomation(DeleteAutomation event, Emitter<RoutineState> emit) {
// try {
// emit(state.copyWith(isLoading: true));
@ -861,9 +919,28 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true));
try {
final projectUuid = projectCubit.state;
final devices = await DevicesManagementApi()
.fetchDevices('', '', projectUuid ?? TempConst.projectId);
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
List<AllDevicesModel> devices = [];
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
var spaceBloc = context.read<SpaceTreeBloc>();
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
}
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
}
emit(state.copyWith(isLoading: false, devices: devices));
} catch (e) {
@ -944,8 +1021,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) {
add(ResetRoutineState());
add(LoadScenes(spaceId, communityId));
add(LoadAutomation(spaceId));
add(const LoadScenes());
add(const LoadAutomation());
} else {
emit(state.copyWith(
isLoading: false,
@ -1056,8 +1133,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
});
}).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<CreateRoutineBloc>();
final createAutomationModel = CreateAutomationModel(
spaceUuid: spaceId,
spaceUuid: spaceBloc.selectedSpaceId,
automationName: state.routineName ?? '',
decisionExpr: state.selectedAutomationOperator,
effectiveTime: EffectiveTime(
@ -1068,14 +1148,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
conditions: conditions,
actions: actions,
);
final projectId = await ProjectManager.getProjectUUID() ?? '';
final result = await SceneApi.updateAutomation(
createAutomationModel, state.automationId ?? '');
createAutomationModel, state.automationId ?? '', projectId);
if (result['success']) {
add(ResetRoutineState());
add(LoadAutomation(spaceId));
add(LoadScenes(spaceId, communityId));
add(LoadAutomation());
add(LoadScenes());
} else {
emit(state.copyWith(
isLoading: false,
@ -1093,6 +1173,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
Future<void> _onGetAutomationDetails(
GetAutomationDetails event, Emitter<RoutineState> emit) async {
try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(state.copyWith(
isLoading: true,
isUpdate: true,
@ -1104,7 +1185,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
final automationDetails =
await SceneApi.getAutomationDetails(event.automationId);
await SceneApi.getAutomationDetails(event.automationId, projectUuid);
final Map<String, Map<String, dynamic>> deviceIfCards = {};
final Map<String, Map<String, dynamic>> deviceThenCards = {};
@ -1282,4 +1363,77 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
));
}
}
Future<void> _onSceneTrigger(
SceneTrigger event, Emitter<RoutineState> emit) async {
emit(state.copyWith(loadingSceneId: event.sceneId));
try {
final success = await SceneApi.triggerScene(event.sceneId!);
if (success) {
emit(state.copyWith(
loadingSceneId: null,
// Add success state if needed
));
// Optional: Add delay to show success feedback
await Future.delayed(const Duration(milliseconds: 500));
} else {
emit(state.copyWith(
loadingSceneId: null,
errorMessage: 'Trigger failed',
));
}
} catch (e) {
emit(state.copyWith(
loadingSceneId: null,
errorMessage: 'Trigger error: ${e.toString()}',
));
}
}
Future<void> _onUpdateAutomationStatus(
UpdateAutomationStatus event, Emitter<RoutineState> emit) async {
// Create a new set safely
final currentLoadingIds = state.loadingAutomationIds;
final newLoadingIds = {...currentLoadingIds!}..add(event.automationId);
emit(state.copyWith(loadingAutomationIds: newLoadingIds));
try {
final projectId = await ProjectManager.getProjectUUID() ?? '';
final success = await SceneApi.updateAutomationStatus(
event.automationId, event.automationStatusUpdate, projectId);
if (success) {
final updatedAutomations = await SceneApi.getAutomationByUnitId(
event.automationStatusUpdate.spaceUuid,
event.communityId,
projectId);
// Remove from loading set safely
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
automations: updatedAutomations,
loadingAutomationIds: updatedLoadingIds,
));
} else {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update failed',
));
}
} catch (e) {
final updatedLoadingIds = {...state.loadingAutomationIds!}
..remove(event.automationId);
emit(state.copyWith(
loadingAutomationIds: updatedLoadingIds,
errorMessage: 'Update error: ${e.toString()}',
));
}
}
}

View File

@ -27,22 +27,24 @@ class AddToThenContainer extends RoutineEvent {
}
class LoadScenes extends RoutineEvent {
final String spaceId;
final String communityId;
// final String spaceId;
// final String communityId;
// final BuildContext context;
const LoadScenes(this.spaceId, this.communityId);
const LoadScenes();
@override
List<Object> get props => [spaceId, communityId];
List<Object> get props => [];
}
class LoadAutomation extends RoutineEvent {
final String spaceId;
// final String spaceId;
// final BuildContext context;
const LoadAutomation(this.spaceId);
const LoadAutomation();
@override
List<Object> get props => [spaceId];
List<Object> get props => [];
}
class AddFunctionToRoutine extends RoutineEvent {
@ -208,3 +210,28 @@ class ResetRoutineState extends RoutineEvent {}
class ClearFunctions extends RoutineEvent {}
class ResetErrorMessage extends RoutineEvent {}
class SceneTrigger extends RoutineEvent {
final String? sceneId;
final String? name;
const SceneTrigger({this.sceneId, this.name});
@override
List<Object> get props => [sceneId!,name!];
}
//updateAutomationStatus
class UpdateAutomationStatus extends RoutineEvent {
final String automationId;
final AutomationStatusUpdate automationStatusUpdate;
final String communityId;
const UpdateAutomationStatus({required this.automationStatusUpdate, required this.automationId, required this.communityId});
@override
List<Object> get props => [automationStatusUpdate];
}

View File

@ -1,6 +1,7 @@
part of 'routine_bloc.dart';
class RoutineState extends Equatable {
final String? loadingSceneId;
final List<Map<String, dynamic>> ifItems;
final List<Map<String, dynamic>> thenItems;
final List<Map<String, String>> availableCards;
@ -25,6 +26,7 @@ class RoutineState extends Equatable {
// final String? automationActionExecutor;
final bool routineTab;
final bool createRoutineView;
final Set<String>? loadingAutomationIds; // Track loading automations
const RoutineState(
{this.ifItems = const [],
@ -47,12 +49,16 @@ class RoutineState extends Equatable {
this.sceneId,
this.automationId,
this.isUpdate,
this.loadingAutomationIds = const <String>{}, // Initialize with empty set
this.loadingSceneId,
this.devices = const [],
// this.automationActionExecutor,
this.routineTab = false,
this.createRoutineView = false});
RoutineState copyWith({
String? loadingSceneId,
Set<String>? loadingAutomationIds,
List<Map<String, dynamic>>? ifItems,
List<Map<String, dynamic>>? thenItems,
List<ScenesModel>? scenes,
@ -79,6 +85,8 @@ class RoutineState extends Equatable {
bool? createRoutineView,
}) {
return RoutineState(
loadingSceneId: loadingSceneId,
loadingAutomationIds: loadingAutomationIds ?? this.loadingAutomationIds,
ifItems: ifItems ?? this.ifItems,
thenItems: thenItems ?? this.thenItems,
scenes: scenes ?? this.scenes,
@ -109,6 +117,7 @@ class RoutineState extends Equatable {
@override
List<Object?> get props => [
loadingAutomationIds,
ifItems,
thenItems,
scenes,
@ -134,3 +143,38 @@ class RoutineState extends Equatable {
createRoutineView
];
}
class SceneInitial extends RoutineState {}
class SceneLoading extends RoutineState {}
class SceneLoaded extends RoutineState {
final List<ScenesModel>? scenesOrAutomation;
const SceneLoaded({this.scenesOrAutomation});
@override
List<Object?> get props => [
scenesOrAutomation,
];
}
class SceneError extends RoutineState {
final String message;
const SceneError({required this.message});
@override
List<Object> get props => [message];
}
class SceneTriggerSuccess extends RoutineState {
final String sceneName;
const SceneTriggerSuccess(this.sceneName);
@override
List<Object> get props => [sceneName];
}
class UpdateAutomationStatusLoading extends RoutineState {
const UpdateAutomationStatusLoading();
}

View File

@ -0,0 +1,156 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CommunityDropdown extends StatelessWidget {
final String? selectedValue;
final List<CommunityModel> communities;
final Function(String?) onChanged;
final TextEditingController _searchController = TextEditingController();
CommunityDropdown({
Key? key,
required this.selectedValue,
required this.onChanged,
required this.communities,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Community",
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 13,
color: ColorsManager.blackColor,
),
),
const SizedBox(height: 8),
SizedBox(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: DropdownButton2<String>(
underline: const SizedBox(),
value: selectedValue,
items: communities.map((community) {
return DropdownMenuItem<String>(
value: community.uuid,
child: Text(
' ${community.name}',
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
);
}).toList(),
onChanged: onChanged,
style: const TextStyle(color: Colors.black),
hint: Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
" Please Select",
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.textGray,
),
),
),
customButton: Container(
height: 45,
decoration: BoxDecoration(
border: Border.all(color: ColorsManager.textGray, width: 1.0),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 5,
child: Text(
selectedValue != null
? " ${communities.firstWhere((element) => element.uuid == selectedValue).name}"
: ' Please Select',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: selectedValue != null
? Colors.black
: ColorsManager.textGray,
),
overflow: TextOverflow.ellipsis,
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
height: 45,
child: const Icon(
Icons.keyboard_arrow_down,
color: ColorsManager.textGray,
),
),
),
],
),
),
dropdownStyleData: DropdownStyleData(
maxHeight: MediaQuery.of(context).size.height * 0.4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
),
dropdownSearchData: DropdownSearchData(
searchController: _searchController,
searchInnerWidgetHeight: 50,
searchInnerWidget: Container(
height: 50,
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: TextFormField(
style: const TextStyle(color: Colors.black),
controller: _searchController,
decoration: InputDecoration(
isDense: true,
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 12,
),
hintText: 'Search for community...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
searchMatchFn: (item, searchValue) {
final communityName =
(item.child as Text).data?.toLowerCase() ?? '';
return communityName
.contains(searchValue.toLowerCase().trim());
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) {
_searchController.clear();
}
},
menuItemStyleData: const MenuItemStyleData(
height: 40,
),
),
))
],
),
);
}
}

View File

@ -0,0 +1,177 @@
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_state.dart';
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
import 'package:syncrow_web/pages/routines/create_new_routines/commu_dropdown.dart';
import 'package:syncrow_web/pages/routines/create_new_routines/space_dropdown.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class CreateNewRoutinesDialog extends StatefulWidget {
const CreateNewRoutinesDialog({super.key});
@override
State<CreateNewRoutinesDialog> createState() =>
_CreateNewRoutinesDialogState();
}
class _CreateNewRoutinesDialogState extends State<CreateNewRoutinesDialog> {
String? _selectedCommunity;
String? _selectedSpace;
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) =>
CreateRoutineBloc()..add(const FetchCommunityEvent()),
child: BlocBuilder<CreateRoutineBloc, CreateRoutineState>(
builder: (context, state) {
final _bloc = BlocProvider.of<CreateRoutineBloc>(context);
final spaces = _bloc.spacesOnlyWithDevices;
final isLoadingCommunities = state is CommunitiesLoadingState;
final isLoadingSpaces = state is SpaceWithDeviceLoadingState;
String spaceHint = 'Select a community first';
if (_selectedCommunity != null) {
if (isLoadingSpaces) {
spaceHint = 'Loading spaces...';
} else if (spaces.isEmpty) {
spaceHint = 'No spaces available';
} else {
spaceHint = 'Select Space';
}
}
return AlertDialog(
backgroundColor: Colors.white,
insetPadding: EdgeInsets.zero,
contentPadding: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
title: Text(
'Create New Routines',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.primaryColor,
),
),
content: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
const Divider(),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: CommunityDropdown(
communities: _bloc.communities,
selectedValue: _selectedCommunity,
onChanged: (String? newValue) {
setState(() {
_selectedCommunity = newValue;
_selectedSpace = null;
});
if (newValue != null) {
_bloc.add(SpaceOnlyWithDevicesEvent(newValue));
}
},
),
),
const SizedBox(height: 5),
Padding(
padding: const EdgeInsets.only(left: 15, right: 15),
child: SpaceDropdown(
hintMessage: spaceHint,
spaces: spaces,
selectedValue: _selectedSpace,
onChanged: (String? newValue) {
setState(() {
_selectedSpace = newValue;
});
},
),
),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
),
child: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
'Cancel',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
color: ColorsManager.blackColor,
),
),
),
),
Padding(
padding: const EdgeInsets.only(
left: 20,
right: 20,
),
child: TextButton(
onPressed: _selectedCommunity != null &&
_selectedSpace != null
? () {
Navigator.of(context).pop({
'community': _selectedCommunity,
'space': _selectedSpace,
});
}
: null,
child: Text(
'Next',
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
color: _selectedCommunity != null &&
_selectedSpace != null
? ColorsManager.blueColor
: Colors.blue.shade100,
),
),
),
),
],
),
const SizedBox(height: 10),
],
),
if (isLoadingCommunities)
const SizedBox(
height: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: CircularProgressIndicator(
color: ColorsManager.primaryColor,
),
),
],
),
),
],
),
);
},
));
}
}

View File

@ -0,0 +1,147 @@
import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class SpaceDropdown extends StatelessWidget {
final List<SpaceModel> spaces;
final String? selectedValue;
final Function(String?)? onChanged;
final String hintMessage;
const SpaceDropdown({
Key? key,
required this.spaces,
required this.selectedValue,
required this.onChanged,
required this.hintMessage,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Space",
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 13,
color: ColorsManager.blackColor,
),
),
const SizedBox(height: 8),
SizedBox(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: DropdownButton2<String>(
underline: const SizedBox(),
value: selectedValue,
items: spaces.map((space) {
return DropdownMenuItem<String>(
value: space.uuid,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
' ${space.name}',
style:
Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 12,
color: ColorsManager.blackColor,
),
),
Text(
' ${space.lastThreeParents}',
style:
Theme.of(context).textTheme.bodyMedium!.copyWith(
fontSize: 12,
),
),
],
),
);
}).toList(),
onChanged: onChanged,
style: TextStyle(color: Colors.black),
hint: Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
hintMessage,
style: Theme.of(context).textTheme.bodySmall!.copyWith(
color: ColorsManager.textGray,
),
),
),
customButton: Container(
height: 45,
decoration: BoxDecoration(
border:
Border.all(color: ColorsManager.textGray, width: 1.0),
borderRadius: BorderRadius.circular(10),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 5,
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Text(
selectedValue != null
? spaces
.firstWhere((e) => e.uuid == selectedValue)
.name
: hintMessage,
style:
Theme.of(context).textTheme.bodySmall!.copyWith(
color: selectedValue != null
? Colors.black
: ColorsManager.textGray,
),
overflow: TextOverflow.ellipsis,
),
),
),
Expanded(
child: Container(
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: const BorderRadius.only(
topRight: Radius.circular(10),
bottomRight: Radius.circular(10),
),
),
height: 45,
child: const Icon(
Icons.keyboard_arrow_down,
color: ColorsManager.textGray,
),
),
),
],
),
),
dropdownStyleData: DropdownStyleData(
maxHeight: MediaQuery.of(context).size.height * 0.4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
),
menuItemStyleData: const MenuItemStyleData(
height: 60,
),
),
),
),
],
),
);
}
}

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