Compare commits

..

109 Commits

Author SHA1 Message Date
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
ec502214f4 Merge pull request #104 from SyncrowIOT/connect_real_time
Connect real time
2025-03-05 15:24:08 +03:00
dcfbe5282e add dev and prod firebase option 2025-03-05 14:21:07 +03: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
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
581dcf7016 changed endpoint for get visitor password and access control 2025-02-16 23:32:46 +04:00
e1609309cf Added ProjectCubit as a dependency in blocs for managing project-level state. 2025-02-15 00:36:20 +04:00
da445e11aa Modified _fetchUserInfo to update the ProjectCubit with the retrieved user's project UUID. 2025-02-15 00:35:34 +04:00
55de7fab0f Introduced ProjectCubit to handle project-related state. 2025-02-15 00:34:38 +04:00
ba44d1d359 firebase configuration and connect device with real-time bd 2025-02-13 14:07:29 +03:00
a623f1c723 Merge pull request #89 from SyncrowIOT/disable_edit
disable_edit_user
2025-02-06 11:17:14 +03:00
c5f5992c18 disable_edit_user 2025-02-06 11:12:43 +03:00
98ad7090d8 Removed prints and unused code 2025-02-06 01:01:21 +03:00
ea08024b82 Merge pull request #87 from SyncrowIOT/bugfix/fix-tag-repeat
Bugfix/update-subspace-tag-value
2025-02-06 00:59:11 +03:00
f6d66185b3 updating tags inside subspace 2025-02-06 00:18:44 +04:00
ead5297ba1 Merge pull request #86 from SyncrowIOT/roles_permissions_bugs
Roles permissions bugs
2025-02-05 21:53:46 +03:00
9a6bf5cbaf privacy policy fixes 2025-02-05 18:16:09 +03:00
51fbe64209 fixes bugs 2025-02-05 16:53:36 +03:00
49fa80e7d8 Merge pull request #85 from SyncrowIOT/bugfix/fix-tag-repeat
Fixed tag repeat
2025-02-05 17:47:47 +04:00
1aa15e5dd6 fixed loading 2025-02-05 17:01:03 +04:00
962f2d6861 Fixed tgag repeat 2025-02-05 16:00:53 +04:00
bd6219f915 Merge pull request #84 from SyncrowIOT/side_tree
Enhanced the side tree design
2025-02-05 11:56:23 +03:00
132cafcaa2 Enhanced the side tree design 2025-02-05 11:52:44 +03:00
8862ad95f3 Merge pull request #83 from SyncrowIOT/bugfix/fix-issue-in-creating-space-with-duplicate
Bugfix/fix-issue-in-creating-space-with-duplicate
2025-02-05 12:09:38 +04:00
af4c0f84cb fixed assign tag issue 2025-02-05 11:15:38 +04:00
c2b77ad1fc fixed issue on duplicate 2025-02-05 11:15:25 +04:00
95cee89b4c Merge pull request #82 from SyncrowIOT/SP-951-FE-Link-Space-Model-Pop-Up
added edit space sibling conflict
2025-02-04 15:57:37 +04:00
d5fcbe2601 added edit space sibling conflict 2025-02-04 14:15:39 +04:00
1fa33a271f added disabled space model button on adding tags and subspace, vice versa 2025-02-04 13:46:11 +04:00
09e2564183 add disabled state to ButtonContentWidget, When disabled, the entire button becomes opaque 2025-02-04 13:29:09 +04:00
5dee6c2842 Merge pull request #81 from SyncrowIOT/bugifx/tag-validation
Bugifx/tag-validation
2025-02-04 12:36:15 +04:00
c5c5088724 removed logs 2025-02-04 11:38:11 +04:00
d1d570b40f Fixed issue in loading space model 2025-02-04 11:36:26 +04:00
a43ff3c07d Merge pull request #80 from SyncrowIOT/side_tree
Side tree
2025-02-04 01:56:52 +03:00
572520eed5 Fixed issues 2025-02-04 01:54:18 +03:00
5e5f127a4b duplicate should be in same vertical offset 2025-02-04 00:12:20 +04:00
6f51c2d2b6 provide all tags on edit space 2025-02-03 22:23:53 +04:00
a18e8443d0 Merge pull request #77 from SyncrowIOT/web_bugs_fixes
Fix bugs related to the user table, privacy policy, and table filter.
2025-02-03 14:39:38 +03:00
72241cba6c added error validation 2025-02-03 14:47:05 +04:00
506531e16a Fetch devices based on selection 2025-02-03 11:15:36 +03:00
ab3edbaf57 Merge pull request #79 from SyncrowIOT/bugfix/fix-duplicate-space
Bugfix/fix-duplicate-space
2025-02-02 23:39:37 +04:00
64e3fb7f34 removed print 2025-02-02 23:38:04 +04:00
e6e46be9b4 fixed issues in create space and duplicate 2025-02-02 23:16:34 +04:00
91dfd53477 fixed issue in creating space 2025-02-02 21:02:58 +04:00
5ab9664318 Merged with dev 2025-02-02 11:24:45 +03:00
d3bf4de0ca Merge pull request #78 from SyncrowIOT/side_tree
fixed issues in the space tree
2025-02-02 02:48:17 +03:00
5ae07688cb fixed issues in the space tree 2025-02-02 02:46:13 +03:00
e6fa9c2391 Merge pull request #76 from SyncrowIOT/bugfix/empty-subspace
add empty subspace validation
2025-01-31 10:58:11 +04:00
b070884bd9 Fix bugs related to the user table, privacy policy, and table filter. 2025-01-30 16:43:45 +03:00
7d05a33c52 Merge pull request #75 from SyncrowIOT/side_tree
Side tree
2025-01-30 12:26:48 +03:00
6e546a4831 Merged with dev 2025-01-30 12:17:06 +03:00
b098202fd8 Merge pull request #74 from SyncrowIOT/bugfix/subspace-name-validatio
add subspace validation
2025-01-30 13:13:10 +04:00
43c17d1c18 Implemented the selection behavior of the side tree 2025-01-30 04:03:54 +03:00
e70b9ea9e2 Merge pull request #73 from SyncrowIOT/bugfix/edit-space
Bugfix/edit space
2025-01-29 12:46:19 +04:00
9e0184f19d Merge pull request #72 from SyncrowIOT/bugfix/edit-subspace
Bugfix/edit-subspace
2025-01-28 13:30:03 +04:00
ea5b6597f5 Merge pull request #71 from SyncrowIOT/feat/update-create-edit-space
Feat/update-create-edit-space
2025-01-27 17:02:01 +04:00
2221d9ae7b Merged with dev 2025-01-26 21:00:05 +03:00
921d352207 Merge pull request #70 from SyncrowIOT/chore/remove-unsupported-param
Chore/remove unsupported param
2025-01-23 10:46:23 +04:00
c5871be990 removed unmatched alignment 2025-01-23 10:21:31 +04:00
2fb6f30ccb Updated pubsepc file 2025-01-23 01:21:13 +03:00
508d8bbaa8 Merged with dev 2025-01-23 01:18:30 +03:00
97bdb1bbb7 Merge pull request #68 from SyncrowIOT/user_agreement_privacy
user_agreement_dialog
2025-01-23 00:40:08 +03:00
7ce0a27af0 Merge pull request #69 from SyncrowIOT/bugfix/space-edit
Bugfix/space edit
2025-01-23 00:37:03 +03:00
bc4af6a237 user_agreement 2025-01-22 17:24:19 +03:00
513175ed1e user_agreement_dialog 2025-01-22 15:44:46 +03:00
5060d2a66d Updated side tree branch 2025-01-21 15:28:59 +03:00
540f569b1f Added spaces devices 2025-01-05 00:21:10 +03:00
a98f7e77a3 Implemented side tree to devices and rountines screen 2025-01-04 17:45:15 +03:00
0341844ea9 SP-859 2024-12-26 12:25:37 +03:00
210 changed files with 7068 additions and 3179 deletions

View File

@ -1,5 +1,9 @@
plugins { plugins {
id "com.android.application" 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 "kotlin-android"
id "dev.flutter.flutter-gradle-plugin" 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 { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false 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 id "org.jetbrains.kotlin.android" version "1.7.10" apply false
} }

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

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 */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
E44A9405B1EB1B638DD05A58 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7ABF0EC746A2D686A0ED574F /* Pods_RunnerTests.framework */; }; 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 */; }; FF49F60EC38658783D8D66DA /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AFAE479A87ECDEBD5D6EB30 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -64,6 +65,7 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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 */ /* End PBXFileReference section */
@ -138,6 +140,7 @@
331C8082294A63A400263BE5 /* RunnerTests */, 331C8082294A63A400263BE5 /* RunnerTests */,
1454C118FFCECEEDF59152D2 /* Pods */, 1454C118FFCECEEDF59152D2 /* Pods */,
20A3C64D2B1CFED5A81C3251 /* Frameworks */, 20A3C64D2B1CFED5A81C3251 /* Frameworks */,
B14AB50E8716720E10D074BD /* GoogleService-Info.plist */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -199,6 +202,7 @@
9705A1C41CF9048500538489 /* Embed Frameworks */, 9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */, 33590C9CD073D3D5EBA02CDE /* [CP] Embed Pods Frameworks */,
7A77858F6F15CB76D2D3A872 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */,
); );
buildRules = ( buildRules = (
); );
@ -264,6 +268,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
F2A3345EC3021060731668D3 /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -303,6 +308,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 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 */ = { 9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; 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

@ -20,15 +20,22 @@ class DialogTextfieldDropdown extends StatefulWidget {
class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> { class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
bool _isOpen = false; bool _isOpen = false;
late OverlayEntry _overlayEntry; OverlayEntry? _overlayEntry;
final TextEditingController _controller = TextEditingController(); final TextEditingController _controller = TextEditingController();
late List<String> _filteredItems; // Filtered items list final FocusNode _focusNode = FocusNode();
List<String> _filteredItems = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_controller.text = widget.initialValue ?? 'Select Tag'; _controller.text = widget.initialValue ?? '';
_filteredItems = List.from(widget.items); // Initialize filtered items _filteredItems = List.from(widget.items);
_focusNode.addListener(() {
if (!_focusNode.hasFocus) {
_closeDropdown();
}
});
} }
void _toggleDropdown() { void _toggleDropdown() {
@ -41,14 +48,17 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
void _openDropdown() { void _openDropdown() {
_overlayEntry = _createOverlayEntry(); _overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry); Overlay.of(context).insert(_overlayEntry!);
_isOpen = true; _isOpen = true;
} }
void _closeDropdown() { void _closeDropdown() {
_overlayEntry.remove(); if (_isOpen && _overlayEntry != null) {
_overlayEntry!.remove();
_overlayEntry = null;
_isOpen = false; _isOpen = false;
} }
}
OverlayEntry _createOverlayEntry() { OverlayEntry _createOverlayEntry() {
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
@ -58,9 +68,7 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
return OverlayEntry( return OverlayEntry(
builder: (context) { builder: (context) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: _closeDropdown,
_closeDropdown();
},
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
child: Stack( child: Stack(
children: [ children: [
@ -72,14 +80,15 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
elevation: 4.0, elevation: 4.0,
child: Container( child: Container(
color: ColorsManager.whiteColors, color: ColorsManager.whiteColors,
constraints: const BoxConstraints( constraints: const BoxConstraints(maxHeight: 200.0),
maxHeight: 200.0, child: StatefulBuilder(
), builder: (context, setStateDropdown) {
child: ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: _filteredItems.length, itemCount: _filteredItems.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = _filteredItems[index]; final item = _filteredItems[index];
return Container( return Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
border: Border( border: Border(
@ -95,7 +104,8 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
.textTheme .textTheme
.bodyMedium .bodyMedium
?.copyWith( ?.copyWith(
color: ColorsManager.textPrimaryColor)), color: ColorsManager
.textPrimaryColor)),
onTap: () { onTap: () {
_controller.text = item; _controller.text = item;
widget.onSelected(item); widget.onSelected(item);
@ -108,6 +118,8 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
), ),
); );
}, },
);
},
), ),
), ),
), ),
@ -122,7 +134,8 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
onTap: _toggleDropdown, onTap: () => FocusScope.of(context).unfocus(),
behavior: HitTestBehavior.opaque,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -135,23 +148,26 @@ class _DialogTextfieldDropdownState extends State<DialogTextfieldDropdown> {
Expanded( Expanded(
child: TextFormField( child: TextFormField(
controller: _controller, controller: _controller,
onChanged: (value) { focusNode: _focusNode,
setState(() { onFieldSubmitted: (value) {
_filteredItems = widget.items
.where((item) =>
item.toLowerCase().contains(value.toLowerCase()))
.toList(); // Filter items dynamically
});
widget.onSelected(value); widget.onSelected(value);
_closeDropdown();
},
onTapOutside: (event) {
widget.onSelected(_controller.text);
_closeDropdown();
}, },
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
decoration: const InputDecoration( decoration: const InputDecoration(
hintText: 'Enter or Select tag', hintText: 'Enter or Select a tag',
border: InputBorder.none, border: InputBorder.none,
), ),
), ),
), ),
const Icon(Icons.arrow_drop_down), GestureDetector(
onTap: _toggleDropdown,
child: const Icon(Icons.arrow_drop_down),
),
], ],
), ),
), ),

View File

@ -56,24 +56,6 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
children: [ children: [
Row( Row(
children: [ 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 // Expand/collapse icon, now wrapped in a GestureDetector for specific onTap
if (widget.children != null && widget.children!.isNotEmpty) if (widget.children != null && widget.children!.isNotEmpty)
GestureDetector( GestureDetector(
@ -84,7 +66,9 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
}); });
}, },
child: Icon( child: Icon(
_isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, _isExpanded
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_right,
color: ColorsManager.lightGrayColor, color: ColorsManager.lightGrayColor,
size: 16.0, // Adjusted size for better alignment size: 16.0, // Adjusted size for better alignment
), ),
@ -101,8 +85,10 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
_capitalizeFirstLetter(widget.title), _capitalizeFirstLetter(widget.title),
style: TextStyle( style: TextStyle(
color: widget.isSelected color: widget.isSelected
? ColorsManager.blackColor // Change color to black when selected ? ColorsManager
: ColorsManager.lightGrayColor, // Gray when not selected .blackColor // Change color to black when selected
: ColorsManager
.lightGrayColor, // Gray when not selected
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
), ),
), ),
@ -111,7 +97,9 @@ class CustomExpansionTileState extends State<CustomExpansionTile> {
], ],
), ),
// The expanded section (children) that shows when the tile is expanded // 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(
padding: const EdgeInsets.only(left: 48.0), // Indented children padding: const EdgeInsets.only(left: 48.0), // Indented children
child: Column( child: Column(

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/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class CustomSearchBar extends StatelessWidget { class CustomSearchBar extends StatefulWidget {
final TextEditingController? controller; final TextEditingController? controller;
final String hintText; final String hintText;
final String? searchQuery;
final Function(String)? onSearchChanged; // Callback for search input changes final Function(String)? onSearchChanged; // Callback for search input changes
const CustomSearchBar({ const CustomSearchBar({
super.key, super.key,
this.controller, this.controller,
this.searchQuery = '',
this.hintText = 'Search', this.hintText = 'Search',
this.onSearchChanged, 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
@ -36,20 +51,20 @@ class CustomSearchBar extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: TextField( child: TextFormField(
controller: controller, controller: widget.controller,
initialValue: widget.searchQuery,
style: const TextStyle( style: const TextStyle(
color: Colors.black, color: Colors.black,
), ),
onChanged: onSearchChanged, // Call the callback on text change onChanged: widget.onSearchChanged, // Call the callback on text change
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
fillColor: ColorsManager.textFieldGreyColor, fillColor: ColorsManager.textFieldGreyColor,
hintText: hintText, hintText: widget.hintText,
hintStyle: TextStyle( hintStyle: Theme.of(context).textTheme.bodyLarge!.copyWith(
color: Color(0xB2999999), color: ColorsManager.lightGrayColor,
fontSize: 12, fontSize: 12,
fontFamily: 'Aftika',
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
height: 0, height: 0,
letterSpacing: -0.24, letterSpacing: -0.24,

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,33 +1,41 @@
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:syncrow_web/firebase_options_dev.dart';
import 'package:syncrow_web/firebase_options_prod.dart';
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.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_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart'; import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/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/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/services/locator.dart'; import 'package:syncrow_web/services/locator.dart';
import 'package:syncrow_web/utils/app_routes.dart'; import 'package:syncrow_web/utils/app_routes.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/theme/theme.dart'; import 'package:syncrow_web/utils/theme/theme.dart';
Future<void> main() async { Future<void> main() async {
try { try {
const environment = const environment =
String.fromEnvironment('FLAVOR', defaultValue: 'development'); String.fromEnvironment('FLAVOR', defaultValue: 'production');
await dotenv.load(fileName: '.env.$environment'); await dotenv.load(fileName: '.env.$environment');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptionsStaging.currentPlatform,
);
initialSetup(); initialSetup();
} catch (_) {} } catch (_) {}
runApp(MyApp()); runApp(MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
MyApp({
super.key, MyApp({super.key});
});
final GoRouter _router = GoRouter( final GoRouter _router = GoRouter(
initialLocation: RoutesConst.auth, initialLocation: RoutesConst.auth,
@ -56,6 +64,9 @@ class MyApp extends StatelessWidget {
BlocProvider<RoutineBloc>( BlocProvider<RoutineBloc>(
create: (context) => RoutineBloc(), create: (context) => RoutineBloc(),
), ),
BlocProvider<SpaceTreeBloc>(
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
),
], ],
child: MaterialApp.router( child: MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
@ -67,6 +78,8 @@ class MyApp extends StatelessWidget {
PointerDeviceKind.unknown, PointerDeviceKind.unknown,
}, },
), ),
key: NavigationService.navigatorKey,
// scaffoldMessengerKey: NavigationService.snackbarKey,
theme: myTheme, theme: myTheme,
routerConfig: _router, routerConfig: _router,
)); ));

84
lib/main_dev.dart Normal file
View File

@ -0,0 +1,84 @@
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/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(
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,6 +3,7 @@ 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_event.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_state.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/access_management/model/password_model.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/pages/common/hour_picker_dialog.dart';
import 'package:syncrow_web/services/access_mang_api.dart'; import 'package:syncrow_web/services/access_mang_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -30,8 +31,9 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
Future<void> _onFetchTableData( Future<void> _onFetchTableData(
FetchTableData event, Emitter<AccessState> emit) async { FetchTableData event, Emitter<AccessState> emit) async {
try { try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(AccessLoaded()); emit(AccessLoaded());
data = await AccessMangApi().fetchVisitorPassword(); data = await AccessMangApi().fetchVisitorPassword(projectUuid);
filteredData = data; filteredData = data;
updateTabsCount(); updateTabsCount();
emit(TableLoaded(data)); emit(TableLoaded(data));

View File

@ -3,6 +3,7 @@ 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_bloc.dart';
import 'package:syncrow_web/pages/access_management/bloc/access_event.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/bloc/access_state.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.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/buttons/search_reset_buttons.dart';
import 'package:syncrow_web/pages/common/custom_table.dart'; import 'package:syncrow_web/pages/common/custom_table.dart';
@ -17,6 +18,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.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/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.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'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout { class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -27,19 +29,19 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
final isLargeScreen = isLargeScreenSize(context); final isLargeScreen = isLargeScreenSize(context);
final isSmallScreen = isSmallScreenSize(context); final isSmallScreen = isSmallScreenSize(context);
final isHalfMediumScreen = isHafMediumScreenSize(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( return WebScaffold(
enableMenuSidebar: false, enableMenuSidebar: false,
appBarTitle: FittedBox( appBarTitle: Text(
child: Text(
'Access Management', 'Access Management',
style: Theme.of(context).textTheme.headlineLarge, style: ResponsiveTextTheme.of(context).deviceManagementTitle,
),
), ),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocProvider( scaffoldBody: BlocProvider(
create: (BuildContext context) => AccessBloc()..add(FetchTableData()), create: (BuildContext context) =>
AccessBloc()..add(FetchTableData()),
child: BlocConsumer<AccessBloc, AccessState>( child: BlocConsumer<AccessBloc, AccessState>(
listener: (context, state) {}, listener: (context, state) {},
builder: (context, state) { builder: (context, state) {
@ -93,11 +95,14 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
return [ return [
item.passwordName, item.passwordName,
item.passwordType.value, item.passwordType.value,
accessBloc.timestampToDate(item.effectiveTime), accessBloc
accessBloc.timestampToDate(item.invalidTime), .timestampToDate(item.effectiveTime),
accessBloc
.timestampToDate(item.invalidTime),
item.deviceName.toString(), item.deviceName.toString(),
item.authorizerEmail.toString(), item.authorizerEmail.toString(),
accessBloc.timestampToDate(item.invalidTime), accessBloc
.timestampToDate(item.invalidTime),
item.passwordStatus.value, item.passwordStatus.value,
]; ];
}).toList(), }).toList(),
@ -108,7 +113,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
}))); })));
} }
Wrap _buildVisitorAdminPasswords(BuildContext context, AccessBloc accessBloc) { Wrap _buildVisitorAdminPasswords(
BuildContext context, AccessBloc accessBloc) {
return Wrap( return Wrap(
spacing: 10, spacing: 10,
runSpacing: 10, runSpacing: 10,
@ -134,7 +140,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
borderRadius: 8, borderRadius: 8,
child: Text( child: Text(
'Create Visitor Password ', 'Create Visitor Password ',
style: context.textTheme.titleSmall!.copyWith(color: Colors.white, fontSize: 12), style: context.textTheme.titleSmall!
.copyWith(color: Colors.white, fontSize: 12),
)), )),
), ),
// Container( // Container(
@ -172,8 +179,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '', description: '',
onSubmitted: (value) { onSubmitted: (value) {
accessBloc.add(FilterDataEvent( accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(), emailAuthorizer:
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex, accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(), passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp, startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp)); endTime: accessBloc.expirationTimeTimeStamp));
@ -191,8 +200,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '', description: '',
onSubmitted: (value) { onSubmitted: (value) {
accessBloc.add(FilterDataEvent( accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(), emailAuthorizer:
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex, accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(), passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp, startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp)); endTime: accessBloc.expirationTimeTimeStamp));
@ -221,7 +232,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
onSearch: () { onSearch: () {
accessBloc.add(FilterDataEvent( accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(), emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex, selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(), passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp, startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp)); endTime: accessBloc.expirationTimeTimeStamp));
@ -249,8 +261,10 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
description: '', description: '',
onSubmitted: (value) { onSubmitted: (value) {
accessBloc.add(FilterDataEvent( accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(), emailAuthorizer:
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex, accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(), passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp, startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp)); endTime: accessBloc.expirationTimeTimeStamp));
@ -274,7 +288,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
onSearch: () { onSearch: () {
accessBloc.add(FilterDataEvent( accessBloc.add(FilterDataEvent(
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(), emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex, selectedTabIndex:
BlocProvider.of<AccessBloc>(context).selectedIndex,
passwordName: accessBloc.passwordName.text.toLowerCase(), passwordName: accessBloc.passwordName.text.toLowerCase(),
startTime: accessBloc.effectiveTimeTimeStamp, startTime: accessBloc.effectiveTimeTimeStamp,
endTime: accessBloc.expirationTimeTimeStamp)); endTime: accessBloc.expirationTimeTimeStamp));

View File

@ -9,9 +9,13 @@ 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/region_model.dart';
import 'package:syncrow_web/pages/auth/model/token.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/auth/model/user_model.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/services/auth_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart'; import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/snack_bar.dart'; import 'package:syncrow_web/utils/snack_bar.dart';
class AuthBloc extends Bloc<AuthEvent, AuthState> { class AuthBloc extends Bloc<AuthEvent, AuthState> {
@ -161,8 +165,14 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
password: event.password, password: event.password,
), ),
); );
} catch (failure) { } on DioException catch (e) {
final errorData = e.response!.data;
String errorMessage = errorData['error']['message'];
if (errorMessage == "Access denied for web platform") {
validate = errorMessage;
} else {
validate = 'Invalid Credentials!'; validate = 'Invalid Credentials!';
}
emit(LoginInitial()); emit(LoginInitial());
return; return;
} }
@ -176,7 +186,6 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
user = UserModel.fromToken(token); user = UserModel.fromToken(token);
loginEmailController.clear(); loginEmailController.clear();
loginPasswordController.clear(); loginPasswordController.clear();
debugPrint("token " + token.accessToken);
emit(LoginSuccess()); emit(LoginSuccess());
} else { } else {
emit(const LoginFailure(error: 'Something went wrong')); emit(const LoginFailure(error: 'Something went wrong'));
@ -423,8 +432,10 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
emit(LoginInitial()); emit(LoginInitial());
} }
static logout() { static Future<void> logout(BuildContext context) async {
const storage = FlutterSecureStorage(); final storage = FlutterSecureStorage();
ProjectManager.clearProjectUUID();
context.read<SpaceTreeBloc>().add(ClearAllData());
storage.deleteAll(); storage.deleteAll();
} }
} }

View File

@ -21,7 +21,9 @@ class LoginWithEmailModel {
return { return {
'email': email, 'email': email,
'password': password, 'password': password,
"platform": "web"
// 'regionUuid': regionUuid, // 'regionUuid': regionUuid,
}; };
} }
} }
//tst@tst.com

View File

@ -0,0 +1,27 @@
class Project {
final String uuid;
final String name;
final String description;
const Project({
required this.uuid,
required this.name,
required this.description,
});
factory Project.fromJson(Map<String, dynamic> json) {
return Project(
uuid: json['uuid'] as String,
name: json['name'] as String,
description: json['description'] as String,
);
}
Map<String, dynamic> toJson() {
return {
'uuid': uuid,
'name': name,
'description': description,
};
}
}

View File

@ -1,3 +1,4 @@
import 'package:syncrow_web/pages/auth/model/project_model.dart';
import 'package:syncrow_web/pages/auth/model/token.dart'; import 'package:syncrow_web/pages/auth/model/token.dart';
class UserModel { class UserModel {
@ -10,6 +11,11 @@ class UserModel {
final String? phoneNumber; final String? phoneNumber;
final bool? isEmailVerified; final bool? isEmailVerified;
final bool? isAgreementAccepted; final bool? isAgreementAccepted;
final bool? hasAcceptedWebAgreement;
final DateTime? webAgreementAcceptedAt;
final UserRole? role;
final Project? project;
UserModel({ UserModel({
required this.uuid, required this.uuid,
required this.email, required this.email,
@ -19,6 +25,10 @@ class UserModel {
required this.phoneNumber, required this.phoneNumber,
required this.isEmailVerified, required this.isEmailVerified,
required this.isAgreementAccepted, required this.isAgreementAccepted,
required this.hasAcceptedWebAgreement,
required this.webAgreementAcceptedAt,
required this.role,
required this.project,
}); });
factory UserModel.fromJson(Map<String, dynamic> json) { factory UserModel.fromJson(Map<String, dynamic> json) {
@ -31,6 +41,13 @@ class UserModel {
phoneNumber: json['phoneNumber'], phoneNumber: json['phoneNumber'],
isEmailVerified: json['isEmailVerified'], isEmailVerified: json['isEmailVerified'],
isAgreementAccepted: json['isAgreementAccepted'], isAgreementAccepted: json['isAgreementAccepted'],
hasAcceptedWebAgreement: json['hasAcceptedWebAgreement'],
webAgreementAcceptedAt: json['webAgreementAcceptedAt'] != null
? DateTime.parse(json['webAgreementAcceptedAt'])
: null,
role: json['role'] != null ? UserRole.fromJson(json['role']) : null,
project:
json['project'] != null ? Project.fromJson(json['project']) : null,
); );
} }
@ -41,6 +58,9 @@ class UserModel {
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken); Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
return UserModel( return UserModel(
hasAcceptedWebAgreement: null,
role: null,
webAgreementAcceptedAt: null,
uuid: tempJson['uuid'].toString(), uuid: tempJson['uuid'].toString(),
email: tempJson['email'], email: tempJson['email'],
firstName: null, firstName: null,
@ -49,6 +69,7 @@ class UserModel {
phoneNumber: null, phoneNumber: null,
isEmailVerified: null, isEmailVerified: null,
isAgreementAccepted: null, isAgreementAccepted: null,
project: null
); );
} }
@ -65,3 +86,26 @@ class UserModel {
}; };
} }
} }
class UserRole {
final String uuid;
final DateTime createdAt;
final DateTime updatedAt;
final String type;
UserRole({
required this.uuid,
required this.createdAt,
required this.updatedAt,
required this.type,
});
factory UserRole.fromJson(Map<String, dynamic> json) {
return UserRole(
uuid: json['uuid'],
createdAt: DateTime.parse(json['createdAt']),
updatedAt: DateTime.parse(json['updatedAt']),
type: json['type'],
);
}
}

View File

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

View File

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

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 'dart:async';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
@ -19,6 +20,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
on<AcControlEvent>(_onAcControl); on<AcControlEvent>(_onAcControl);
on<AcBatchControlEvent>(_onAcBatchControl); on<AcBatchControlEvent>(_onAcBatchControl);
on<AcFactoryResetEvent>(_onFactoryReset); on<AcFactoryResetEvent>(_onFactoryReset);
on<AcStatusUpdated>(_onAcStatusUpdated);
} }
FutureOr<void> _onFetchAcStatus( FutureOr<void> _onFetchAcStatus(
@ -28,12 +30,64 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status); deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ACStatusLoaded(deviceStatus)); emit(ACStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(AcsFailedState(error: e.toString())); 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( FutureOr<void> _onAcControl(
AcControlEvent event, Emitter<AcsState> emit) async { AcControlEvent event, Emitter<AcsState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);

View File

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

View File

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

View File

@ -1,7 +1,13 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.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/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/services/devices_mang_api.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
part 'device_managment_event.dart'; part 'device_managment_event.dart';
part 'device_managment_state.dart'; part 'device_managment_state.dart';
@ -34,7 +40,25 @@ class DeviceManagementBloc
FetchDevices event, Emitter<DeviceManagementState> emit) async { FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading()); emit(DeviceManagementLoading());
try { try {
final devices = await DevicesManagementApi().fetchDevices(); List<AllDevicesModel> devices = [];
_devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) {
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));
}
}
}
_selectedDevices.clear(); _selectedDevices.clear();
_devices = devices; _devices = devices;
_filteredDevices = devices; _filteredDevices = devices;
@ -288,8 +312,8 @@ class DeviceManagementBloc
event.unitName!.isEmpty || event.unitName!.isEmpty ||
(device.spaces != null && (device.spaces != null &&
device.spaces!.isNotEmpty && device.spaces!.isNotEmpty &&
device.spaces![0].spaceName device.spaces![0].spaceName!
!.toLowerCase() .toLowerCase()
.contains(event.unitName!.toLowerCase())); .contains(event.unitName!.toLowerCase()));
final matchesProductName = event.productName == null || final matchesProductName = event.productName == null ||
event.productName!.isEmpty || event.productName!.isEmpty ||

View File

@ -7,7 +7,15 @@ abstract class DeviceManagementEvent extends Equatable {
List<Object?> get props => []; List<Object?> get props => [];
} }
class FetchDevices extends DeviceManagementEvent {} class FetchDevices extends DeviceManagementEvent {
// final Map<String, List<String>> selectedCommunitiesSpaces;
// final String spaceId;
final BuildContext context;
const FetchDevices(this.context);
@override
List<Object?> get props => [context];
}
class FilterDevices extends DeviceManagementEvent { class FilterDevices extends DeviceManagementEvent {
final String filter; final String filter;

View File

@ -3,11 +3,11 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_spa
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
import 'package:syncrow_web/pages/routiens/models/ac/ac_function.dart'; import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/one_gang_switch/one_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/three_gang_switch/three_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switch/three_gang_switch.dart';
import 'package:syncrow_web/pages/routiens/models/gang_switches/two_gang_switch/two_gang_switch.dart'; import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/enum/device_types.dart'; import 'package:syncrow_web/utils/enum/device_types.dart';
@ -148,9 +148,7 @@ class AllDevicesModel {
productName = json['productName']?.toString(); productName = json['productName']?.toString();
if (json['spaces'] != null && json['spaces'] is List) { if (json['spaces'] != null && json['spaces'] is List) {
spaces = (json['spaces'] as List) spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
.map((space) => DeviceSpaceModel.fromJson(space))
.toList();
} }
} }
@ -198,8 +196,7 @@ SOS
String tempIcon = ''; String tempIcon = '';
if (type == DeviceType.LightBulb) { if (type == DeviceType.LightBulb) {
tempIcon = Assets.lightBulb; tempIcon = Assets.lightBulb;
} else if (type == DeviceType.CeilingSensor || } else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
type == DeviceType.WallSensor) {
tempIcon = Assets.sensors; tempIcon = Assets.sensors;
} else if (type == DeviceType.AC) { } else if (type == DeviceType.AC) {
tempIcon = Assets.ac; tempIcon = Assets.ac;
@ -254,34 +251,25 @@ SOS
case '1G': case '1G':
return [ return [
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''), OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
OneGangCountdownFunction( OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
case '2G': case '2G':
return [ return [
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown1Function( TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
TwoGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
case '3G': case '3G':
return [ return [
ThreeGangSwitch1Function( ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch2Function( ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangSwitch3Function( ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
deviceId: uuid ?? '', deviceName: name ?? ''), ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown1Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown2Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
ThreeGangCountdown3Function(
deviceId: uuid ?? '', deviceName: name ?? ''),
]; ];
default: default:

View File

@ -1,14 +1,16 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.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/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/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/device_managment/shared/navigate_home_grid_view.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart'; import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart'; import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/view/routines_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/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.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/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'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout { class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
@ -19,17 +21,17 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return MultiBlocProvider( return MultiBlocProvider(
providers: [ providers: [
BlocProvider( BlocProvider(
create: (context) => DeviceManagementBloc()..add(FetchDevices()), create: (context) =>
DeviceManagementBloc()..add(FetchDevices(context)),
), ),
], ],
child: WebScaffold( child: WebScaffold(
appBarTitle: FittedBox( appBarTitle: Text(
child: Text(
'Device Management', 'Device Management',
style: Theme.of(context).textTheme.headlineLarge, style: ResponsiveTextTheme.of(context).deviceManagementTitle,
), ),
), centerBody:
centerBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) { BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -41,12 +43,16 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
context context
.read<RoutineBloc>() .read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: false)); .add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.read<DeviceManagementBloc>().add(FetchDevices(context));
}, },
child: Text( child: Text(
'Devices', 'Devices',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: !state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor, color: !state.routineTab
fontWeight: !state.routineTab ? FontWeight.w700 : FontWeight.w400, ? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
!state.routineTab ? FontWeight.w700 : FontWeight.w400,
), ),
), ),
), ),
@ -55,13 +61,18 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
backgroundColor: null, backgroundColor: null,
), ),
onPressed: () { onPressed: () {
context.read<RoutineBloc>().add(const TriggerSwitchTabsEvent(isRoutineTab: true)); context
.read<RoutineBloc>()
.add(const TriggerSwitchTabsEvent(isRoutineTab: true));
}, },
child: Text( child: Text(
'Routines', 'Routines',
style: context.textTheme.titleMedium?.copyWith( style: context.textTheme.titleMedium?.copyWith(
color: state.routineTab ? ColorsManager.whiteColors : ColorsManager.grayColor, color: state.routineTab
fontWeight: state.routineTab ? FontWeight.w700 : FontWeight.w400, ? ColorsManager.whiteColors
: ColorsManager.grayColor,
fontWeight:
state.routineTab ? FontWeight.w700 : FontWeight.w400,
), ),
), ),
), ),
@ -69,7 +80,8 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
); );
}), }),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
scaffoldBody: BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) { scaffoldBody:
BlocBuilder<RoutineBloc, RoutineState>(builder: (context, state) {
if (state.routineTab) { if (state.routineTab) {
return const RoutinesView(); return const RoutinesView();
} }
@ -80,11 +92,12 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>( return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
builder: (context, deviceState) { builder: (context, deviceState) {
if (deviceState is DeviceManagementLoading) { if (deviceState is DeviceManagementLoading) {
return const Center(child: CircularProgressIndicator()); return const DeviceManagementBody(devices: []);
} else if (deviceState is DeviceManagementLoaded) { } else if (deviceState is DeviceManagementLoaded) {
return DeviceManagementBody(devices: deviceState.devices); return DeviceManagementBody(devices: deviceState.devices);
} else if (deviceState is DeviceManagementFiltered) { } else if (deviceState is DeviceManagementFiltered) {
return DeviceManagementBody(devices: deviceState.filteredDevices); return DeviceManagementBody(
devices: deviceState.filteredDevices);
} else { } else {
return const Center(child: Text('Error fetching Devices')); return const Center(child: Text('Error fetching Devices'));
} }

View File

@ -8,6 +8,7 @@ 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/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_batch_control_dialog.dart';
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart'; import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.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/format_date_time.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
@ -59,10 +60,23 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control'; final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
return Column( return Row(
children: [
Expanded(child: SpaceTreeView(
onSelect: () {
context.read<DeviceManagementBloc>().add(FetchDevices(context));
},
)),
Expanded(
flex: 4,
child: state is DeviceManagementLoading
? const Center(child: CircularProgressIndicator())
: Column(
children: [ children: [
Container( Container(
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15), padding: isLargeScreenSize(context)
? const EdgeInsets.all(30)
: const EdgeInsets.all(15),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -71,14 +85,16 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
tabs: tabs, tabs: tabs,
selectedIndex: selectedIndex, selectedIndex: selectedIndex,
onTabChanged: (index) { onTabChanged: (index) {
context.read<DeviceManagementBloc>().add(SelectedFilterChanged(index)); context
.read<DeviceManagementBloc>()
.add(SelectedFilterChanged(index));
}, },
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const DeviceSearchFilters(), const DeviceSearchFilters(),
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
height: 45, // height: 45,
width: 125, width: 125,
decoration: containerDecoration, decoration: containerDecoration,
child: Center( child: Center(
@ -93,7 +109,9 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
), ),
); );
} else if (selectedDevices.length > 1) { } else if (selectedDevices.length > 1) {
final productTypes = selectedDevices.map((device) => device.productType).toSet(); final productTypes = selectedDevices
.map((device) => device.productType)
.toSet();
if (productTypes.length == 1) { if (productTypes.length == 1) {
showDialog( showDialog(
context: context, context: context,
@ -122,13 +140,17 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
), ),
Expanded( Expanded(
child: Padding( child: Padding(
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15), padding: isLargeScreenSize(context)
? const EdgeInsets.all(30)
: const EdgeInsets.all(15),
child: DynamicTable( child: DynamicTable(
withSelectAll: true, withSelectAll: true,
cellDecoration: containerDecoration, cellDecoration: containerDecoration,
onRowSelected: (index, isSelected, row) { onRowSelected: (index, isSelected, row) {
final selectedDevice = devicesToShow[index]; final selectedDevice = devicesToShow[index];
context.read<DeviceManagementBloc>().add(SelectDevice(selectedDevice)); context
.read<DeviceManagementBloc>()
.add(SelectDevice(selectedDevice));
}, },
withCheckBox: true, withCheckBox: true,
size: MediaQuery.of(context).size, size: MediaQuery.of(context).size,
@ -147,31 +169,45 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
data: devicesToShow.map((device) { data: devicesToShow.map((device) {
final combinedSpaceNames = device.spaces != null final combinedSpaceNames = device.spaces != null
? device.spaces!.map((space) => space.spaceName).join(' > ') + ? device.spaces!.map((space) => space.spaceName).join(' > ') +
(device.community != null ? ' > ${device.community!.name}' : '') (device.community != null
? ' > ${device.community!.name}'
: '')
: (device.community != null ? device.community!.name : ''); : (device.community != null ? device.community!.name : '');
return [ return [
device.name ?? '', device.name ?? '',
device.productName ?? '', device.productName ?? '',
device.uuid ?? '', device.uuid ?? '',
(device.spaces != null && device.spaces!.isNotEmpty) ? device.spaces![0].spaceName : '', (device.spaces != null && device.spaces!.isNotEmpty)
? device.spaces![0].spaceName
: '',
combinedSpaceNames, combinedSpaceNames,
device.batteryLevel != null ? '${device.batteryLevel}%' : '-', device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.createTime ?? 0) * 1000)), formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.createTime ?? 0) * 1000)),
device.online == true ? 'Online' : 'Offline', device.online == true ? 'Online' : 'Offline',
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.updateTime ?? 0) * 1000)), formatDateTime(DateTime.fromMillisecondsSinceEpoch(
(device.updateTime ?? 0) * 1000)),
]; ];
}).toList(), }).toList(),
onSelectionChanged: (selectedRows) { onSelectionChanged: (selectedRows) {
context.read<DeviceManagementBloc>().add(UpdateSelection(selectedRows)); context
.read<DeviceManagementBloc>()
.add(UpdateSelection(selectedRows));
}, },
initialSelectedIds: initialSelectedIds: context
context.read<DeviceManagementBloc>().selectedDevices.map((device) => device.uuid!).toList(), .read<DeviceManagementBloc>()
.selectedDevices
.map((device) => device.uuid!)
.toList(),
isEmpty: devicesToShow.isEmpty, isEmpty: devicesToShow.isEmpty,
), ),
), ),
) )
], ],
),
),
],
); );
}, },
); );

View File

@ -12,8 +12,7 @@ class DeviceSearchFilters extends StatefulWidget {
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState(); State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
} }
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
with HelperResponsiveLayout {
final TextEditingController communityController = TextEditingController(); final TextEditingController communityController = TextEditingController();
final TextEditingController unitNameController = TextEditingController(); final TextEditingController unitNameController = TextEditingController();
final TextEditingController productNameController = TextEditingController(); final TextEditingController productNameController = TextEditingController();
@ -27,8 +26,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchField("Space Name", unitNameController, 200), _buildSearchField("Space Name", unitNameController, 200),
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchField( _buildSearchField("Device Name / Product Name", productNameController, 300),
"Device Name / Product Name", productNameController, 300),
const SizedBox(width: 20), const SizedBox(width: 20),
_buildSearchResetButtons(), _buildSearchResetButtons(),
], ],
@ -53,8 +51,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
); );
} }
Widget _buildSearchField( Widget _buildSearchField(String title, TextEditingController controller, double width) {
String title, TextEditingController controller, double width) {
return Container( return Container(
child: StatefulTextField( child: StatefulTextField(
title: title, title: title,
@ -88,7 +85,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
productNameController.clear(); productNameController.clear();
context.read<DeviceManagementBloc>() context.read<DeviceManagementBloc>()
..add(ResetFilters()) ..add(ResetFilters())
..add(FetchDevices()); ..add(FetchDevices(context));
}, },
); );
} }

View File

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

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; 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/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 { abstract class CeilingSensorEvent extends Equatable {
const CeilingSensorEvent(); const CeilingSensorEvent();
@ -83,3 +84,12 @@ class CeilingFactoryResetEvent extends CeilingSensorEvent {
@override @override
List<Object> get props => [devicesId, factoryResetModel]; 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 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.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<CurtainControl>(_onCurtainControl);
on<CurtainBatchControl>(_onCurtainBatchControl); on<CurtainBatchControl>(_onCurtainBatchControl);
on<CurtainFactoryReset>(_onFactoryReset); on<CurtainFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
FutureOr<void> _onFetchDeviceStatus( FutureOr<void> _onFetchDeviceStatus(
@ -24,7 +26,7 @@ class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus = _checkStatus(status.status[0].value); deviceStatus = _checkStatus(status.status[0].value);
emit(CurtainStatusLoaded(deviceStatus)); 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( FutureOr<void> _onCurtainControl(
CurtainControl event, Emitter<CurtainState> emit) async { CurtainControl event, Emitter<CurtainState> emit) async {
final oldValue = deviceStatus; final oldValue = deviceStatus;

View File

@ -60,3 +60,7 @@ class CurtainFactoryReset extends CurtainEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; 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 // ignore_for_file: invalid_use_of_visible_for_testing_member
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_event.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<DoorLockControl>(_onDoorLockControl);
on<UpdateLockEvent>(_updateLock); on<UpdateLockEvent>(_updateLock);
on<DoorLockFactoryReset>(_onFactoryReset); 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( FutureOr<void> _onFetchDeviceStatus(
@ -28,6 +62,8 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
DoorLockStatusModel.fromJson(event.deviceId, status.status); DoorLockStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(DoorLockStatusLoaded(deviceStatus)); emit(DoorLockStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(DoorLockControlError(e.toString())); emit(DoorLockControlError(e.toString()));

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; 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/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 { sealed class DoorLockEvent extends Equatable {
const DoorLockEvent(); const DoorLockEvent();
@ -51,3 +52,10 @@ class DoorLockFactoryReset extends DoorLockEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; 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 'dart:async';
import 'package:bloc/bloc.dart'; 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_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.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<GarageDoorFetchBatchStatusEvent>(_onFetchBatchStatus);
on<GarageDoorFactoryResetEvent>(_onFactoryReset); on<GarageDoorFactoryResetEvent>(_onFactoryReset);
on<EditGarageDoorScheduleEvent>(_onEditSchedule); 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()); emit(GarageDoorLoadingState());
try { try {
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId); var response =
await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status); deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status);
_listenToChanges(deviceId);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} catch (e) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); 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()); emit(GarageDoorLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus =
GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status);
emit(GarageDoorBatchStatusLoaded(deviceStatus)); emit(GarageDoorBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(GarageDoorBatchControlError(e.toString())); emit(GarageDoorBatchControlError(e.toString()));
} }
} }
Future<void> _addSchedule(AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { Future<void> _addSchedule(
AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try { try {
ScheduleEntry newSchedule = ScheduleEntry( ScheduleEntry newSchedule = ScheduleEntry(
category: event.category, category: event.category,
@ -71,9 +109,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn), function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId); bool success =
await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId);
if (success) { if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1')); add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else { } else {
emit(GarageDoorLoadedState(status: deviceStatus)); 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( 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 { try {
final updatedSchedules = deviceStatus.schedules?.map((schedule) { final updatedSchedules = deviceStatus.schedules?.map((schedule) {
if (schedule.scheduleId == event.scheduleId) { 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 { try {
bool success = await DevicesManagementApi().deleteScheduleRecord(deviceStatus.uuid, event.scheduleId); bool success = await DevicesManagementApi()
.deleteScheduleRecord(deviceStatus.uuid, event.scheduleId);
if (success) { if (success) {
final updatedSchedules = final updatedSchedules = deviceStatus.schedules
deviceStatus.schedules?.where((schedule) => schedule.scheduleId != event.scheduleId).toList(); ?.where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules); deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} else { } 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()); emit(ScheduleGarageLoadingState());
try { try {
List<ScheduleModel> schedules = List<ScheduleModel> schedules = await DevicesManagementApi()
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category); .getDeviceSchedules(deviceStatus.uuid, event.category);
deviceStatus = deviceStatus.copyWith(schedules: schedules); deviceStatus = deviceStatus.copyWith(schedules: schedules);
emit( emit(
GarageDoorLoadedState( 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith(selectedTime: event.selectedTime)); 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
List<bool> updatedDays = List.from(currentState.selectedDays); List<bool> updatedDays = List.from(currentState.selectedDays);
updatedDays[event.dayIndex] = event.isSelected; 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { 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; final currentState = state;
if (currentState is GarageDoorLoadedState) { if (currentState is GarageDoorLoadedState) {
emit(currentState.copyWith( 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()); emit(GarageDoorReportsLoadingState());
try { 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 to = DateTime.now().millisecondsSinceEpoch;
final DeviceReport records = 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)); emit(GarageDoorReportsState(deviceReport: records));
} catch (e) { } catch (e) {
emit(GarageDoorReportsFailedState(error: e.toString())); 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; final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false;
_updateLocalValue(event.code, event.value); _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)); emit(GarageDoorLoadedState(status: deviceStatus));
} }
void _handleUpdate(GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) { void _handleUpdate(
GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }
@ -253,9 +315,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
late bool status; late bool status;
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: 500));
if (isBatch) { if (isBatch) {
status = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); status = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
status = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); status = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!status) { 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()); emit(GarageDoorLoadingState());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(const GarageDoorErrorState(message: 'Failed to reset device')); emit(const GarageDoorErrorState(message: 'Failed to reset device'));
} else { } 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) { // if (deviceStatus.countdown1 != 0) {
try { try {
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay + Duration(minutes: 10)); deviceStatus = deviceStatus.copyWith(
delay: deviceStatus.delay + Duration(minutes: 10));
emit(GarageDoorLoadedState(status: deviceStatus)); 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) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); 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) { // if (deviceStatus.countdown1 != 0) {
try { try {
if (deviceStatus.delay.inMinutes > 10) { 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)); 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) { } catch (e) {
emit(GarageDoorErrorState(message: e.toString())); emit(GarageDoorErrorState(message: e.toString()));
} }
//} //}
} }
void _garageDoorControlEvent(GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async { void _garageDoorControlEvent(
final oldValue = event.code == 'countdown_1' ? deviceStatus.countdown1 : deviceStatus.switch1; GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async {
final oldValue = event.code == 'countdown_1'
? deviceStatus.countdown1
: deviceStatus.switch1;
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
final success = await _runDeBouncer( 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) { switch (code) {
case 'switch_1': case 'switch_1':
if (oldValue is bool) { if (oldValue is bool) {
@ -336,7 +416,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break; break;
case 'countdown_1': case 'countdown_1':
if (oldValue is int) { if (oldValue is int) {
deviceStatus = deviceStatus.copyWith(countdown1: oldValue, delay: Duration(seconds: oldValue)); deviceStatus = deviceStatus.copyWith(
countdown1: oldValue, delay: Duration(seconds: oldValue));
} }
break; break;
// Add other cases if needed // Add other cases if needed
@ -358,7 +439,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
break; break;
case 'countdown_1': case 'countdown_1':
if (value is int) { if (value is int) {
deviceStatus = deviceStatus.copyWith(countdown1: value, delay: Duration(seconds: value)); deviceStatus = deviceStatus.copyWith(
countdown1: value, delay: Duration(seconds: value));
} }
break; break;
case 'countdown_alarm': case 'countdown_alarm':
@ -401,7 +483,8 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
return super.close(); return super.close();
} }
FutureOr<void> _onEditSchedule(EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async { FutureOr<void> _onEditSchedule(
EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
try { try {
ScheduleEntry newSchedule = ScheduleEntry( ScheduleEntry newSchedule = ScheduleEntry(
scheduleId: event.scheduleId, scheduleId: event.scheduleId,
@ -410,9 +493,11 @@ class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
function: Status(code: 'switch_1', value: event.functionOn), function: Status(code: 'switch_1', value: event.functionOn),
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays), days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
); );
bool success = await DevicesManagementApi().editScheduleRecord(deviceId, newSchedule); bool success = await DevicesManagementApi()
.editScheduleRecord(deviceId, newSchedule);
if (success) { if (success) {
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1')); add(FetchGarageDoorSchedulesEvent(
deviceId: deviceId, category: 'switch_1'));
} else { } else {
emit(GarageDoorLoadedState(status: deviceStatus)); emit(GarageDoorLoadedState(status: deviceStatus));
} }

View File

@ -3,6 +3,7 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.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/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 { abstract class GarageDoorEvent extends Equatable {
const GarageDoorEvent(); const GarageDoorEvent();
@ -25,7 +26,8 @@ class GarageDoorControlEvent extends GarageDoorEvent {
final dynamic value; final dynamic value;
final String code; 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 @override
List<Object?> get props => [deviceId, value]; List<Object?> get props => [deviceId, value];
@ -121,7 +123,8 @@ class FetchGarageDoorRecordsEvent extends GarageDoorEvent {
final String deviceId; final String deviceId;
final String code; final String code;
const FetchGarageDoorRecordsEvent({required this.deviceId, required this.code}); const FetchGarageDoorRecordsEvent(
{required this.deviceId, required this.code});
@override @override
List<Object?> get props => [deviceId, code]; List<Object?> get props => [deviceId, code];
@ -232,3 +235,10 @@ class GarageDoorFactoryResetEvent extends GarageDoorEvent {
@override @override
List<Object?> get props => [factoryReset, deviceId]; 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 'dart:async';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
@ -16,6 +17,7 @@ class MainDoorSensorBloc
on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus); on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus);
on<MainDoorSensorReportsEvent>(_fetchReports); on<MainDoorSensorReportsEvent>(_fetchReports);
on<MainDoorSensorFactoryReset>(_factoryReset); on<MainDoorSensorFactoryReset>(_factoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late MainDoorSensorStatusModel deviceStatus; late MainDoorSensorStatusModel deviceStatus;
@ -28,7 +30,7 @@ class MainDoorSensorBloc
final status = await DevicesManagementApi() final status = await DevicesManagementApi()
.getDeviceStatus(event.deviceId) .getDeviceStatus(event.deviceId)
.then((value) => value.status); .then((value) => value.status);
_listenToChanges(event.deviceId);
deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status); deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status);
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus)); emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -156,4 +158,35 @@ class MainDoorSensorBloc
emit(MainDoorSensorFailedState(error: e.toString())); 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: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'; import '../../all_devices/models/factory_reset_model.dart';
@ -71,3 +72,10 @@ class MainDoorSensorFactoryReset extends MainDoorSensorEvent {
MainDoorSensorFactoryReset( MainDoorSensorFactoryReset(
{required this.deviceId, required this.factoryReset}); {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 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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_event.dart';
part 'one_gang_glass_switch_state.dart'; part 'one_gang_glass_switch_state.dart';
class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> { class OneGangGlassSwitchBloc
extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
OneGangGlassStatusModel deviceStatus; OneGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
OneGangGlassSwitchBloc({required String deviceId}) OneGangGlassSwitchBloc({required String deviceId})
: deviceStatus = OneGangGlassStatusModel(uuid: deviceId, switch1: false, countDown: 0), : deviceStatus = OneGangGlassStatusModel(
uuid: deviceId, switch1: false, countDown: 0),
super(OneGangGlassSwitchInitial()) { super(OneGangGlassSwitchInitial()) {
on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus); on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
on<OneGangGlassSwitchControl>(_onControl); on<OneGangGlassSwitchControl>(_onControl);
on<OneGangGlassSwitchBatchControl>(_onBatchControl); on<OneGangGlassSwitchBatchControl>(_onBatchControl);
on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<OneGangGlassFactoryResetEvent>(_onFactoryReset); on<OneGangGlassFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(OneGangGlassSwitchFetchDeviceEvent event,
OneGangGlassSwitchFetchDeviceEvent event, Emitter<OneGangGlassSwitchState> emit) async { Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId);
deviceStatus =
OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); 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); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _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()); emit(OneGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(OneGangGlassSwitchError('Failed to reset device')); emit(OneGangGlassSwitchError('Failed to reset device'));
} else { } 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); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -83,11 +125,14 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
OneGangGlassSwitchFetchBatchStatusEvent event, Emitter<OneGangGlassSwitchState> emit) async { OneGangGlassSwitchFetchBatchStatusEvent event,
Emitter<OneGangGlassSwitchState> emit) async {
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = OneGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(OneGangGlassSwitchError(e.toString())); emit(OneGangGlassSwitchError(e.toString()));
@ -117,9 +162,11 @@ class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassS
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!response) { 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); _updateLocalValue(code, oldValue);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} }

View File

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

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.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<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<WallLightSwitchBatchControl>(_onBatchControl); on<WallLightSwitchBatchControl>(_onBatchControl);
on<WallLightFactoryReset>(_onFactoryReset); on<WallLightFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late WallLightStatusModel deviceStatus; late WallLightStatusModel deviceStatus;
@ -31,12 +33,44 @@ class WallLightSwitchBloc
deviceStatus = deviceStatus =
WallLightStatusModel.fromJson(event.deviceId, status.status); WallLightStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(WallLightSwitchStatusLoaded(deviceStatus)); emit(WallLightSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(WallLightSwitchError(e.toString())); 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( FutureOr<void> _onControl(
WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async { WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; 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/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 { class WallLightSwitchEvent extends Equatable {
@override @override
@ -57,3 +58,10 @@ class WallLightFactoryReset extends WallLightSwitchEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; 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 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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_event.dart';
part 'three_gang_glass_switch_state.dart'; part 'three_gang_glass_switch_state.dart';
class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> { class ThreeGangGlassSwitchBloc
extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
ThreeGangGlassStatusModel deviceStatus; ThreeGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
@ -29,21 +31,57 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
on<ThreeGangGlassSwitchBatchControl>(_onBatchControl); on<ThreeGangGlassSwitchBatchControl>(_onBatchControl);
on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<ThreeGangGlassFactoryReset>(_onFactoryReset); on<ThreeGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus( Future<void> _onFetchDeviceStatus(ThreeGangGlassSwitchFetchDeviceEvent event,
ThreeGangGlassSwitchFetchDeviceEvent event, Emitter<ThreeGangGlassSwitchState> emit) async { Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); 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); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _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); final oldValue = _getValueByCode(event.code);
_updateLocalValue(event.code, event.value); _updateLocalValue(event.code, event.value);
@ -76,21 +115,26 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
} }
Future<void> _onFetchBatchStatus( Future<void> _onFetchBatchStatus(
ThreeGangGlassSwitchFetchBatchStatusEvent event, Emitter<ThreeGangGlassSwitchState> emit) async { ThreeGangGlassSwitchFetchBatchStatusEvent event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds); final status =
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status); await DevicesManagementApi().getBatchStatus(event.deviceIds);
deviceStatus = ThreeGangGlassStatusModel.fromJson(
event.deviceIds.first, status.status);
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(ThreeGangGlassSwitchError(e.toString())); emit(ThreeGangGlassSwitchError(e.toString()));
} }
} }
Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event, Emitter<ThreeGangGlassSwitchState> emit) async { Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event,
Emitter<ThreeGangGlassSwitchState> emit) async {
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId); final response = await DevicesManagementApi()
.factoryReset(event.factoryReset, event.deviceId);
if (!response) { if (!response) {
emit(ThreeGangGlassSwitchError('Failed')); emit(ThreeGangGlassSwitchError('Failed'));
} else { } else {
@ -124,9 +168,11 @@ class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGang
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value)); response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value));
} }
if (!response) { 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); _updateLocalValue(code, oldValue);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
} }

View File

@ -49,3 +49,10 @@ class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
required this.factoryReset, 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 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.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/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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'; 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<LivingRoomBatchControl>(_livingRoomBatchControl);
on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl); on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl);
on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset); on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event, FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
@ -32,6 +34,7 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
LivingRoomStatusModel.fromJson(event.deviceId, status.status); LivingRoomStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(deviceId);
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
@ -144,6 +147,9 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
await DevicesManagementApi().getBatchStatus(event.devicesIds); await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = deviceStatus =
LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status); LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status);
// for (var deviceId in event.devicesIds) {
// _listenToChanges(deviceId);
// }
emit(LivingRoomDeviceStatusLoaded(deviceStatus)); emit(LivingRoomDeviceStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(LivingRoomDeviceManagementError(e.toString())); emit(LivingRoomDeviceManagementError(e.toString()));
@ -185,4 +191,34 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
emit(LivingRoomDeviceManagementError(e.toString())); 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 @override
List<Object> get props => [uuid, factoryReset]; 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 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:meta/meta.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/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart'; part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart'; part 'two_gang_glass_switch_state.dart';
@ -14,7 +13,6 @@ class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> { extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
TwoGangGlassStatusModel deviceStatus; TwoGangGlassStatusModel deviceStatus;
Timer? _timer; Timer? _timer;
TwoGangGlassSwitchBloc({required String deviceId}) TwoGangGlassSwitchBloc({required String deviceId})
: deviceStatus = TwoGangGlassStatusModel( : deviceStatus = TwoGangGlassStatusModel(
uuid: deviceId, uuid: deviceId,
@ -28,6 +26,7 @@ class TwoGangGlassSwitchBloc
on<TwoGangGlassSwitchBatchControl>(_onBatchControl); on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus); on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
on<TwoGangGlassFactoryReset>(_onFactoryReset); on<TwoGangGlassFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event, Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event,
@ -38,12 +37,44 @@ class TwoGangGlassSwitchBloc
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = deviceStatus =
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status); TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(event.deviceId);
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus)); emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangGlassSwitchError(e.toString())); 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, Future<void> _onControl(TwoGangGlassSwitchControl event,
Emitter<TwoGangGlassSwitchState> emit) async { Emitter<TwoGangGlassSwitchState> emit) async {
final oldValue = _getValueByCode(event.code); final oldValue = _getValueByCode(event.code);
@ -178,4 +209,37 @@ class TwoGangGlassSwitchBloc
_timer?.cancel(); _timer?.cancel();
return super.close(); 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, 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/constants/assets.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart'; import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout { class TwoGangGlassSwitchControlView extends StatelessWidget
with HelperResponsiveLayout {
final String deviceId; final String deviceId;
const TwoGangGlassSwitchControlView({required this.deviceId, super.key}); const TwoGangGlassSwitchControlView({required this.deviceId, super.key});
@ -14,8 +15,8 @@ class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (context) => create: (context) => TwoGangGlassSwitchBloc(deviceId: deviceId)
TwoGangGlassSwitchBloc(deviceId: deviceId)..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)), ..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>( child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
builder: (context, state) { builder: (context, state) {
if (state is TwoGangGlassSwitchLoading) { if (state is TwoGangGlassSwitchLoading) {
@ -23,16 +24,16 @@ class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiv
} else if (state is TwoGangGlassSwitchStatusLoaded) { } else if (state is TwoGangGlassSwitchStatusLoaded) {
return _buildStatusControls(context, state.status); return _buildStatusControls(context, state.status);
} else if (state is TwoGangGlassSwitchError) { } else if (state is TwoGangGlassSwitchError) {
return const Center(child: Text('Error fetching status')); return Center(child: Text(state.message));
} else { } else {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
}, },
), ));
);
} }
Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel status) { Widget _buildStatusControls(
BuildContext context, TwoGangGlassStatusModel status) {
final isExtraLarge = isExtraLargeScreenSize(context); final isExtraLarge = isExtraLargeScreenSize(context);
final isLarge = isLargeScreenSize(context); final isLarge = isLargeScreenSize(context);
final isMedium = isMediumScreenSize(context); final isMedium = isMediumScreenSize(context);

View File

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.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<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
on<TwoGangSwitchBatchControl>(_onBatchControl); on<TwoGangSwitchBatchControl>(_onBatchControl);
on<TwoGangFactoryReset>(_onFactoryReset); on<TwoGangFactoryReset>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
late TwoGangStatusModel deviceStatus; late TwoGangStatusModel deviceStatus;
@ -26,8 +28,8 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status); deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
_listenToChanges(emit);
emit(TwoGangSwitchStatusLoaded(deviceStatus)); emit(TwoGangSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
emit(TwoGangSwitchError(e.toString())); emit(TwoGangSwitchError(e.toString()));
@ -174,4 +176,34 @@ class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
emit(TwoGangSwitchError(e.toString())); 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:equatable/equatable.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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 { class TwoGangSwitchEvent extends Equatable {
@override @override
@ -57,3 +58,10 @@ class TwoGangFactoryReset extends TwoGangSwitchEvent {
@override @override
List<Object> get props => [deviceId, factoryReset]; 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 'dart:async';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter_bloc/flutter_bloc.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.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); var response = await DevicesManagementApi().getDeviceStatus(deviceId);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
// _listenToChanges(); _listenToChanges(emit);
} catch (e) { } catch (e) {
emit(WallSensorFailedState(error: e.toString())); emit(WallSensorFailedState(error: e.toString()));
return; return;
@ -38,10 +39,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
// Fetch batch status // Fetch batch status
FutureOr<void> _fetchWallSensorBatchControl( FutureOr<void> _fetchWallSensorBatchControl(
WallSensorFetchBatchStatusEvent event, Emitter<WallSensorState> emit) async { WallSensorFetchBatchStatusEvent event,
Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingInitialState()); emit(WallSensorLoadingInitialState());
try { try {
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds); var response =
await DevicesManagementApi().getBatchStatus(event.devicesIds);
deviceStatus = WallSensorModel.fromJson(response.status); deviceStatus = WallSensorModel.fromJson(response.status);
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} catch (e) { } catch (e) {
@ -49,26 +52,30 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
} }
} }
// _listenToChanges() { _listenToChanges(Emitter<WallSensorState> emit) {
// try { try {
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); DatabaseReference ref =
// Stream<DatabaseEvent> stream = ref.onValue; FirebaseDatabase.instance.ref('device-status/$deviceId');
Stream<DatabaseEvent> stream = ref.onValue;
// stream.listen((DatabaseEvent event) { stream.listen((DatabaseEvent event) {
// Map<dynamic, dynamic> usersMap = event.snapshot.value as Map<dynamic, dynamic>; Map<dynamic, dynamic> usersMap =
// List<StatusModel> statusList = []; event.snapshot.value as Map<dynamic, dynamic>;
List<Status> statusList = [];
// usersMap['status'].forEach((element) { usersMap['status'].forEach((element) {
// statusList.add(StatusModel(code: element['code'], value: element['value'])); statusList
// }); .add(Status(code: element['code'], value: element['value']));
});
// deviceStatus = WallSensorModel.fromJson(statusList); deviceStatus = WallSensorModel.fromJson(statusList);
// add(WallSensorUpdatedEvent()); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
// }); });
// } catch (_) {} } catch (_) {}
// } }
void _changeValue(WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async { void _changeValue(
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus)); emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
if (event.code == 'far_detection') { if (event.code == 'far_detection') {
deviceStatus.farDetection = event.value; deviceStatus.farDetection = event.value;
@ -125,7 +132,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try { try {
late bool response; late bool response;
if (isBatch) { if (isBatch) {
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value); response = await DevicesManagementApi()
.deviceBatchControl(deviceId, code, value);
} else { } else {
response = await DevicesManagementApi() response = await DevicesManagementApi()
.deviceControl(deviceId, Status(code: code, value: value)); .deviceControl(deviceId, Status(code: code, value: value));
@ -150,7 +158,8 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
try { try {
// await DevicesManagementApi.getDeviceReportsByDate( // await DevicesManagementApi.getDeviceReportsByDate(
// deviceId, event.code, from.toString(), to.toString()) // 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)); emit(DeviceReportsState(deviceReport: value, code: event.code));
}); });
} catch (e) { } 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)); emit(WallSensorShowDescriptionState(description: event.description));
} }
void _backToGridView(BackToGridViewEvent event, Emitter<WallSensorState> emit) { void _backToGridView(
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
emit(WallSensorUpdateState(wallSensorModel: deviceStatus)); emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
} }

View File

@ -4,6 +4,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.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/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.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<EditWaterHeaterScheduleEvent>(_onEditSchedule);
on<DeleteScheduleEvent>(_onDeleteSchedule); on<DeleteScheduleEvent>(_onDeleteSchedule);
on<UpdateScheduleEntryEvent>(_onUpdateSchedule); on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
on<StatusUpdated>(_onStatusUpdated);
} }
late WaterHeaterStatusModel deviceStatus; late WaterHeaterStatusModel deviceStatus;
@ -78,7 +80,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
final updatedDays = List<bool>.from(currentState.selectedDays); final updatedDays = List<bool>.from(currentState.selectedDays);
updatedDays[event.index] = event.value; updatedDays[event.index] = event.value;
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime)); emit(currentState.copyWith(
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
} }
FutureOr<void> _updateFunctionOn( FutureOr<void> _updateFunctionOn(
@ -86,7 +89,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
) { ) {
final currentState = state as WaterHeaterDeviceStatusLoaded; 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( FutureOr<void> _updateScheduleEvent(
@ -101,7 +105,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
)); ));
} }
if (event.scheduleMode == ScheduleModes.countdown) { 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( emit(currentState.copyWith(
scheduleMode: ScheduleModes.countdown, scheduleMode: ScheduleModes.countdown,
@ -111,11 +116,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
countdownRemaining: countdownRemaining, countdownRemaining: countdownRemaining,
)); ));
if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) { if (!currentState.isCountdownActive! &&
countdownRemaining > Duration.zero) {
_startCountdownTimer(emit, countdownRemaining); _startCountdownTimer(emit, countdownRemaining);
} }
} else if (event.scheduleMode == ScheduleModes.inching) { } 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( emit(currentState.copyWith(
scheduleMode: ScheduleModes.inching, scheduleMode: ScheduleModes.inching,
@ -217,7 +224,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
try { try {
final status = await DevicesManagementApi().deviceControl( final status = await DevicesManagementApi().deviceControl(
event.deviceId, event.deviceId,
Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0), Status(
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
); );
if (!status) { if (!status) {
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.')); emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
@ -235,8 +243,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
emit(WaterHeaterLoadingState()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status =
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
if (deviceStatus.scheduleMode == ScheduleModes.countdown) { if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
final countdownRemaining = Duration( final countdownRemaining = Duration(
@ -300,11 +310,42 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
isInchingActive: false, isInchingActive: false,
)); ));
} }
_listenToChanges(event.deviceId);
} catch (e) { } catch (e) {
emit(WaterHeaterFailedState(error: e.toString())); 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( void _startCountdownTimer(
Emitter<WaterHeaterState> emit, Emitter<WaterHeaterState> emit,
Duration countdownRemaining, Duration countdownRemaining,
@ -334,8 +375,10 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
if (currentState.countdownRemaining != null && currentState.countdownRemaining! > Duration.zero) { if (currentState.countdownRemaining != null &&
final newRemaining = currentState.countdownRemaining! - const Duration(minutes: 1); currentState.countdownRemaining! > Duration.zero) {
final newRemaining =
currentState.countdownRemaining! - const Duration(minutes: 1);
if (newRemaining <= Duration.zero) { if (newRemaining <= Duration.zero) {
_countdownTimer?.cancel(); _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); _updateLocalValue(code, oldValue);
if (state is WaterHeaterDeviceStatusLoaded) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -477,12 +521,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
return super.close(); return super.close();
} }
FutureOr<void> _getSchedule(GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async { FutureOr<void> _getSchedule(
GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
emit(ScheduleLoadingState()); emit(ScheduleLoadingState());
try { try {
List<ScheduleModel> schedules = List<ScheduleModel> schedules = await DevicesManagementApi()
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category); .getDeviceSchedules(deviceStatus.uuid, event.category);
emit(WaterHeaterDeviceStatusLoaded( emit(WaterHeaterDeviceStatusLoaded(
deviceStatus, deviceStatus,
@ -514,7 +559,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState()); // emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, currentState.status.uuid); bool success = await DevicesManagementApi()
.addScheduleRecord(newSchedule, currentState.status.uuid);
if (success) { if (success) {
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid)); 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) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;
@ -594,11 +641,13 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
// emit(ScheduleLoadingState()); // emit(ScheduleLoadingState());
bool success = await DevicesManagementApi().deleteScheduleRecord(currentState.status.uuid, event.scheduleId); bool success = await DevicesManagementApi()
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
if (success) { if (success) {
final updatedSchedules = final updatedSchedules = currentState.schedules
currentState.schedules.where((schedule) => schedule.scheduleId != event.scheduleId).toList(); .where((schedule) => schedule.scheduleId != event.scheduleId)
.toList();
emit(currentState.copyWith(schedules: updatedSchedules)); emit(currentState.copyWith(schedules: updatedSchedules));
} else { } 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()); emit(WaterHeaterLoadingState());
try { try {
final status = await DevicesManagementApi().getBatchStatus(event.devicesUuid); final status =
deviceStatus = WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status); await DevicesManagementApi().getBatchStatus(event.devicesUuid);
deviceStatus = WaterHeaterStatusModel.fromJson(
event.devicesUuid.first, status.status);
emit(WaterHeaterDeviceStatusLoaded(deviceStatus)); emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
} catch (e) { } 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) { if (state is WaterHeaterDeviceStatusLoaded) {
final currentState = state as WaterHeaterDeviceStatusLoaded; final currentState = state as WaterHeaterDeviceStatusLoaded;

View File

@ -54,6 +54,15 @@ final class WaterHeaterFetchStatusEvent extends WaterHeaterEvent {
final class DecrementCountdownEvent 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 class AddScheduleEvent extends WaterHeaterEvent {
final List<bool> selectedDays; final List<bool> selectedDays;
final TimeOfDay time; final TimeOfDay time;

View File

@ -1,4 +1,5 @@
import 'package:bloc/bloc.dart'; 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_reports.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.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<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus);
on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports); on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports);
on<WaterLeakFactoryResetEvent>(_onFactoryReset); on<WaterLeakFactoryResetEvent>(_onFactoryReset);
on<StatusUpdated>(_onStatusUpdated);
} }
Future<void> _onFetchWaterLeakStatus( Future<void> _onFetchWaterLeakStatus(
@ -30,12 +32,43 @@ class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
final response = final response =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status); deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
_listenToChanges();
emit(WaterLeakLoadedState(deviceStatus!)); emit(WaterLeakLoadedState(deviceStatus!));
} catch (e) { } catch (e) {
emit(WaterLeakErrorState(e.toString())); 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( Future<void> _onControl(
WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async { WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async {
final oldValue = deviceStatus!.watersensorState; final oldValue = deviceStatus!.watersensorState;

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; 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/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 { abstract class WaterLeakEvent extends Equatable {
const WaterLeakEvent(); const WaterLeakEvent();
@ -17,6 +18,13 @@ class FetchWaterLeakStatusEvent extends WaterLeakEvent {
List<Object> get props => [deviceId]; 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 { class WaterLeakControlEvent extends WaterLeakEvent {
final String deviceId; final String deviceId;
final String code; final String code;

View File

@ -1,56 +1,104 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:go_router/go_router.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/auth/model/user_model.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_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.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/home/home_model/home_item_model.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/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/services/home_api.dart'; import 'package:syncrow_web/services/home_api.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/routes_const.dart'; import 'package:syncrow_web/utils/constants/routes_const.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> { class HomeBloc extends Bloc<HomeEvent, HomeState> {
final Graph graph = Graph()..isTree = true; // final Graph graph = Graph()..isTree = true;
final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration(); // final BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
List<Node> sourcesList = []; // List<Node> sourcesList = [];
List<Node> destinationsList = []; // List<Node> destinationsList = [];
UserModel? user; UserModel? user;
String terms = '';
String policy = '';
HomeBloc() : super((HomeInitial())) { HomeBloc() : super((HomeInitial())) {
on<CreateNewNode>(_createNode); // on<CreateNewNode>(_createNode);
on<FetchUserInfo>(_fetchUserInfo); on<FetchUserInfo>(_fetchUserInfo);
on<FetchTermEvent>(_fetchTerms);
on<FetchPolicyEvent>(_fetchPolicy);
on<ConfirmUserAgreementEvent>(_confirmUserAgreement);
} }
void _createNode(CreateNewNode event, Emitter<HomeState> emit) async { // void _createNode(CreateNewNode event, Emitter<HomeState> emit) async {
emit(HomeInitial()); // emit(HomeInitial());
sourcesList.add(event.sourceNode); // sourcesList.add(event.sourceNode);
destinationsList.add(event.destinationNode); // destinationsList.add(event.destinationNode);
for (int i = 0; i < sourcesList.length; i++) { // for (int i = 0; i < sourcesList.length; i++) {
graph.addEdge(sourcesList[i], destinationsList[i]); // graph.addEdge(sourcesList[i], destinationsList[i]);
} // }
builder // builder
..siblingSeparation = (100) // ..siblingSeparation = (100)
..levelSeparation = (150) // ..levelSeparation = (150)
..subtreeSeparation = (150) // ..subtreeSeparation = (150)
..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM); // ..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM);
emit(HomeUpdateTree(graph: graph, builder: builder)); // emit(HomeUpdateTree(graph: graph, builder: builder));
} // }
Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async { Future _fetchUserInfo(FetchUserInfo event, Emitter<HomeState> emit) async {
try { try {
var uuid = var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
user = await HomeApi().fetchUserInfo(uuid); user = await HomeApi().fetchUserInfo(uuid);
if (user != null && user!.project != null) {
await ProjectManager.setProjectUUID(user!.project!.uuid);
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>().add(InitialEvent());
}
add(FetchTermEvent());
add(FetchPolicyEvent());
emit(HomeInitial()); emit(HomeInitial());
} catch (e) { } catch (e) {
return; return;
} }
} }
Future _fetchTerms(FetchTermEvent event, Emitter<HomeState> emit) async {
try {
emit(LoadingHome());
terms = await HomeApi().fetchTerms();
emit(HomeInitial());
} catch (e) {
return;
}
}
Future _fetchPolicy(FetchPolicyEvent event, Emitter<HomeState> emit) async {
try {
emit(LoadingHome());
policy = await HomeApi().fetchPolicy();
emit(HomeInitial());
} catch (e) {
debugPrint("Error fetching policy: $e");
return;
}
}
Future _confirmUserAgreement(ConfirmUserAgreementEvent event, Emitter<HomeState> emit) async {
try {
emit(LoadingHome());
var uuid = await const FlutterSecureStorage().read(key: UserModel.userUuidKey);
policy = await HomeApi().confirmUserAgreements(uuid);
emit(PolicyAgreement());
} catch (e) {
return;
}
}
// static Future fetchUserInfo() async { // static Future fetchUserInfo() async {
// try { // try {
// var uuid = // var uuid =
@ -63,10 +111,11 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
List<HomeItemModel> homeItems = [ List<HomeItemModel> homeItems = [
HomeItemModel( HomeItemModel(
title: 'Access', title: 'Access Management',
icon: Assets.accessIcon, icon: Assets.accessIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.accessManagementPage); context.go(RoutesConst.accessManagementPage);
}, },
color: null, color: null,
@ -76,21 +125,24 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
icon: Assets.spaseManagementIcon, icon: Assets.spaseManagementIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
context.go(RoutesConst.spacesManagementPage); context.go(RoutesConst.spacesManagementPage);
}, },
color: ColorsManager.primaryColor, color: ColorsManager.primaryColor,
), ),
HomeItemModel( HomeItemModel(
title: 'Devices', title: 'Devices Management',
icon: Assets.devicesIcon, icon: Assets.devicesIcon,
active: true, active: true,
onPress: (context) { onPress: (context) {
context.read<SpaceTreeBloc>().add(ClearCachedData());
BlocProvider.of<RoutineBloc>(context) BlocProvider.of<RoutineBloc>(context)
.add(const TriggerSwitchTabsEvent(isRoutineTab: false)); .add(const TriggerSwitchTabsEvent(isRoutineTab: false));
context.go(RoutesConst.deviceManagementPage); context.go(RoutesConst.deviceManagementPage);
}, },
color: ColorsManager.primaryColor, color: ColorsManager.primaryColor,
), ),
// HomeItemModel( // HomeItemModel(
// title: 'Move in', // title: 'Move in',
// icon: Assets.moveinIcon, // icon: Assets.moveinIcon,

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
abstract class HomeEvent extends Equatable { abstract class HomeEvent extends Equatable {
const HomeEvent(); const HomeEvent();
@ -8,16 +8,22 @@ abstract class HomeEvent extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class CreateNewNode extends HomeEvent { // class CreateNewNode extends HomeEvent {
final Node sourceNode; // final Node sourceNode;
final Node destinationNode; // final Node destinationNode;
const CreateNewNode( // const CreateNewNode(
{required this.sourceNode, required this.destinationNode}); // {required this.sourceNode, required this.destinationNode});
@override // @override
List<Object> get props => [sourceNode, destinationNode]; // List<Object> get props => [sourceNode, destinationNode];
} // }
class FetchUserInfo extends HomeEvent { class FetchUserInfo extends HomeEvent {
const FetchUserInfo(); const FetchUserInfo();
} }
class FetchTermEvent extends HomeEvent {}
class FetchPolicyEvent extends HomeEvent {}
class ConfirmUserAgreementEvent extends HomeEvent {}

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
abstract class HomeState extends Equatable { abstract class HomeState extends Equatable {
const HomeState(); const HomeState();
@ -8,19 +8,25 @@ abstract class HomeState extends Equatable {
List<Object> get props => []; List<Object> get props => [];
} }
class LoadingHome extends HomeState {}
class HomeInitial extends HomeState {} class HomeInitial extends HomeState {}
class HomeCounterState extends HomeState { class TermsAgreement extends HomeState {}
final int counter;
const HomeCounterState(this.counter);
}
class HomeUpdateTree extends HomeState { class PolicyAgreement extends HomeState {}
final Graph graph;
final BuchheimWalkerConfiguration builder;
const HomeUpdateTree({required this.graph, required this.builder}); // class HomeCounterState extends HomeState {
// final int counter;
// const HomeCounterState(this.counter);
// }
@override // class HomeUpdateTree extends HomeState {
List<Object> get props => [graph, builder]; // final Graph graph;
} // final BuchheimWalkerConfiguration builder;
// const HomeUpdateTree({required this.graph, required this.builder});
// @override
// List<Object> get props => [graph, builder];
// }

View File

@ -0,0 +1,180 @@
import 'package:flutter/material.dart';
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_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';
class AgreementAndPrivacyDialog extends StatefulWidget {
final String terms;
final String policy;
const AgreementAndPrivacyDialog({
super.key,
required this.terms,
required this.policy,
});
@override
_AgreementAndPrivacyDialogState createState() =>
_AgreementAndPrivacyDialogState();
}
class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
final ScrollController _scrollController = ScrollController();
bool _isAtEnd = false;
int _currentPage = 1;
@override
void initState() {
super.initState();
_scrollController.addListener(_onScroll);
WidgetsBinding.instance
.addPostFrameCallback((_) => _checkScrollRequirement());
}
void _checkScrollRequirement() {
final scrollPosition = _scrollController.position;
if (scrollPosition.maxScrollExtent <= 0) {
setState(() {
_isAtEnd = true;
});
}
}
@override
void dispose() {
_scrollController.removeListener(_onScroll);
_scrollController.dispose();
super.dispose();
}
void _onScroll() {
if (_scrollController.position.atEdge) {
final isAtBottom = _scrollController.position.pixels ==
_scrollController.position.maxScrollExtent;
if (isAtBottom && !_isAtEnd) {
setState(() {
_isAtEnd = true;
});
}
}
}
String get _dialogTitle =>
_currentPage == 1 ? 'User Agreement' : 'Privacy Policy';
String get _dialogContent => _currentPage == 1 ? widget.terms : widget.policy;
final String staticText =
'<h5 style="color: #FF5722;">If you cancel you will be logged out.</h5>';
Widget _buildScrollableContent() {
return Container(
padding: const EdgeInsets.all(40),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.75,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Scrollbar(
thumbVisibility: true,
trackVisibility: true,
interactive: true,
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(25),
child: Html(
data: "$_dialogContent $staticText",
onLinkTap: (url, attributes, element) async {
if (url != null) {
final uri = Uri.parse(url);
await launchUrl(uri, mode: LaunchMode.externalApplication);
}
},
style: {
"body": Style(
fontSize: FontSize(14),
color: Colors.black87,
lineHeight: LineHeight(1.5),
),
},
),
),
),
);
}
Widget _buildActionButton() {
final String buttonText = _currentPage == 2 ? "I Agree" : "Next";
return InkWell(
onTap: _isAtEnd
? () {
if (_currentPage == 1) {
setState(() {
_currentPage = 2;
_isAtEnd = false;
_scrollController.jumpTo(0);
WidgetsBinding.instance
.addPostFrameCallback((_) => _checkScrollRequirement());
});
} else {
Navigator.of(context).pop(true);
}
}
: null,
child: Text(
buttonText,
style: TextStyle(
color: _isAtEnd ? ColorsManager.secondaryColor : Colors.grey,
),
),
);
}
@override
Widget build(BuildContext context) {
return Dialog(
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
_dialogTitle,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: ColorsManager.secondaryColor,
),
),
),
const Divider(),
_buildScrollableContent(),
const Divider(),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
InkWell(
onTap: () {
AuthBloc.logout(context);
context.go(RoutesConst.auth);
},
child: const Text("Cancel"),
),
_buildActionButton(),
],
),
),
],
),
);
}
}

View File

@ -41,8 +41,7 @@ class HomeMobilePage extends StatelessWidget {
SizedBox(height: size.height * 0.05), SizedBox(height: size.height * 0.05),
const Text( const Text(
'ACCESS YOUR APPS', 'ACCESS YOUR APPS',
style: style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
), ),
const SizedBox(height: 30), const SizedBox(height: 30),
Expanded( Expanded(
@ -51,9 +50,8 @@ class HomeMobilePage extends StatelessWidget {
height: size.height * 0.6, height: size.height * 0.6,
width: size.width * 0.68, width: size.width * 0.68,
child: GridView.builder( child: GridView.builder(
itemCount: 8, itemCount: 3,
gridDelegate: gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,
crossAxisSpacing: 20.0, crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0, mainAxisSpacing: 20.0,
@ -65,8 +63,7 @@ class HomeMobilePage extends StatelessWidget {
active: homeItems[index]['active'], active: homeItems[index]['active'],
name: homeItems[index]['title'], name: homeItems[index]['title'],
img: homeItems[index]['icon'], img: homeItems[index]['icon'],
onTap: () => onTap: () => homeBloc.homeItems[index].onPress(context),
homeBloc.homeItems[index].onPress(context),
); );
}, },
), ),
@ -97,33 +94,33 @@ class HomeMobilePage extends StatelessWidget {
'icon': Assets.devicesIcon, 'icon': Assets.devicesIcon,
'active': true, 'active': true,
}, },
{ // {
'title': 'Move in', // 'title': 'Move in',
'icon': Assets.moveinIcon, // 'icon': Assets.moveinIcon,
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Construction', // 'title': 'Construction',
'icon': Assets.constructionIcon, // 'icon': Assets.constructionIcon,
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Energy', // 'title': 'Energy',
'icon': Assets.energyIcon, // 'icon': Assets.energyIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Integrations', // 'title': 'Integrations',
'icon': Assets.integrationsIcon, // 'icon': Assets.integrationsIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
{ // {
'title': 'Asset', // 'title': 'Asset',
'icon': Assets.assetIcon, // 'icon': Assets.assetIcon,
'color': ColorsManager.slidingBlueColor.withOpacity(0.2), // 'color': ColorsManager.slidingBlueColor.withOpacity(0.2),
'active': false, // 'active': false,
}, // },
]; ];
} }

View File

@ -1,24 +1,67 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/view/agreement_and_privacy_dialog.dart';
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; import 'package:syncrow_web/pages/home/bloc/home_state.dart';
import 'package:syncrow_web/pages/home/view/home_card.dart'; import 'package:syncrow_web/pages/home/view/home_card.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/web_layout/web_scaffold.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class HomeWebPage extends StatelessWidget { class HomeWebPage extends StatefulWidget {
const HomeWebPage({super.key}); const HomeWebPage({super.key});
@override
State<HomeWebPage> createState() => _HomeWebPageState();
}
class _HomeWebPageState extends State<HomeWebPage> {
// Flag to track whether the dialog is already shown.
bool _dialogShown = false;
@override
void initState() {
super.initState();
final homeBloc = BlocProvider.of<HomeBloc>(context);
homeBloc.add(FetchUserInfo());
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; Size size = MediaQuery.of(context).size;
final homeBloc = BlocProvider.of<HomeBloc>(context);
return PopScope( return PopScope(
canPop: false, canPop: false,
onPopInvoked: (didPop) => false, onPopInvoked: (didPop) => false,
child: BlocConsumer<HomeBloc, HomeState>( child: BlocConsumer<HomeBloc, HomeState>(
listener: (BuildContext context, state) {}, listener: (BuildContext context, state) {
if (state is HomeInitial) {
if (homeBloc.user!.hasAcceptedWebAgreement == false && !_dialogShown) {
_dialogShown = true; // Set the flag to true to indicate the dialog is showing.
Future.delayed(const Duration(seconds: 1), () {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AgreementAndPrivacyDialog(
terms: homeBloc.terms,
policy: homeBloc.policy,
);
},
).then((v) {
_dialogShown = false;
if (v != null) {
homeBloc.add(ConfirmUserAgreementEvent());
homeBloc.add(const FetchUserInfo());
}
});
});
}
}
},
builder: (context, state) { builder: (context, state) {
final homeBloc = BlocProvider.of<HomeBloc>(context);
return WebScaffold( return WebScaffold(
enableMenuSidebar: false, enableMenuSidebar: false,
appBarTitle: Row( appBarTitle: Row(
@ -32,7 +75,10 @@ class HomeWebPage extends StatelessWidget {
scaffoldBody: SizedBox( scaffoldBody: SizedBox(
height: size.height, height: size.height,
width: size.width, width: size.width,
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -51,9 +97,9 @@ class HomeWebPage extends StatelessWidget {
height: size.height * 0.6, height: size.height * 0.6,
width: size.width * 0.68, width: size.width * 0.68,
child: GridView.builder( child: GridView.builder(
itemCount: 3, //8 itemCount: 3, // Change this count if needed.
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, crossAxisCount: 3, // Adjust as needed.
crossAxisSpacing: 20.0, crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0, mainAxisSpacing: 20.0,
childAspectRatio: 1.5, childAspectRatio: 1.5,
@ -72,9 +118,12 @@ class HomeWebPage extends StatelessWidget {
), ),
], ],
), ),
],
),
), ),
); );
}, },
)); ),
);
} }
} }

View File

@ -1,185 +1,185 @@
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:graphview/GraphView.dart'; // import 'package:graphview/GraphView.dart';
import 'package:syncrow_web/pages/home/bloc/home_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/home/bloc/home_event.dart';
import 'package:syncrow_web/pages/home/bloc/home_state.dart'; // import 'package:syncrow_web/pages/home/bloc/home_state.dart';
class TreeWidget extends StatelessWidget { // class TreeWidget extends StatelessWidget {
const TreeWidget({super.key}); // const TreeWidget({super.key});
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
// final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context); // // final HomeBloc homeBloc = BlocProvider.of<HomeBloc>(context);
String firstNodeName = ''; // String firstNodeName = '';
String secondNodeName = ''; // String secondNodeName = '';
return SafeArea( // return SafeArea(
child: Container( // child: Container(
padding: const EdgeInsets.all(24), // padding: const EdgeInsets.all(24),
width: MediaQuery.sizeOf(context).width, // width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height, // height: MediaQuery.sizeOf(context).height,
alignment: AlignmentDirectional.center, // alignment: AlignmentDirectional.center,
child: Column( // child: Column(
mainAxisSize: MainAxisSize.max, // mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
children: [ // children: [
BlocBuilder<HomeBloc, HomeState>(builder: (context, state) { // BlocBuilder<HomeBloc, HomeState>(builder: (context, state) {
if (state is HomeInitial) { // if (state is HomeInitial) {
return Wrap( // return Wrap(
children: [ // children: [
SizedBox( // SizedBox(
width: 100, // width: 100,
child: TextFormField( // child: TextFormField(
decoration: const InputDecoration( // decoration: const InputDecoration(
labelText: "Subtree separation"), // labelText: "Subtree separation"),
onChanged: (text) { // onChanged: (text) {
firstNodeName = text; // firstNodeName = text;
}, // },
), // ),
), // ),
const SizedBox( // const SizedBox(
width: 8, // width: 8,
), // ),
Container( // Container(
width: 100, // width: 100,
child: TextFormField( // child: TextFormField(
decoration: InputDecoration(labelText: "Node Name"), // decoration: InputDecoration(labelText: "Node Name"),
onChanged: (text) { // onChanged: (text) {
secondNodeName = text; // secondNodeName = text;
}, // },
), // ),
), // ),
ElevatedButton( // ElevatedButton(
onPressed: () { // onPressed: () {
final node1 = Node.Id(firstNodeName); // final node1 = Node.Id(firstNodeName);
final node2 = Node.Id(secondNodeName); // final node2 = Node.Id(secondNodeName);
context.read<HomeBloc>().add(CreateNewNode( // context.read<HomeBloc>().add(CreateNewNode(
sourceNode: node1, destinationNode: node2)); // sourceNode: node1, destinationNode: node2));
}, // },
child: Text("Add"), // child: Text("Add"),
) // )
], // ],
); // );
} // }
if (state is HomeUpdateTree) { // if (state is HomeUpdateTree) {
return Expanded( // return Expanded(
child: InteractiveViewer( // child: InteractiveViewer(
constrained: false, // constrained: false,
boundaryMargin: const EdgeInsets.all(100), // boundaryMargin: const EdgeInsets.all(100),
minScale: 0.01, // minScale: 0.01,
maxScale: 5.6, // maxScale: 5.6,
child: GraphView( // child: GraphView(
graph: state.graph, // graph: state.graph,
algorithm: BuchheimWalkerAlgorithm( // algorithm: BuchheimWalkerAlgorithm(
state.builder, TreeEdgeRenderer(state.builder)), // state.builder, TreeEdgeRenderer(state.builder)),
paint: Paint() // paint: Paint()
..color = Colors.green // ..color = Colors.green
..strokeWidth = 1 // ..strokeWidth = 1
..style = PaintingStyle.stroke, // ..style = PaintingStyle.stroke,
builder: (Node node) { // builder: (Node node) {
// I can decide what widget should be shown here based on the id // // I can decide what widget should be shown here based on the id
var nodeName = node.key!.value; // var nodeName = node.key!.value;
return rectangleWidget(nodeName, node, context); // return rectangleWidget(nodeName, node, context);
}, // },
)), // )),
); // );
} else { // } else {
return Container(); // return Container();
} // }
}) // })
], // ],
), // ),
), // ),
); // );
} // }
} // }
Widget rectangleWidget(String text, Node node, BuildContext blocContext) { // Widget rectangleWidget(String text, Node node, BuildContext blocContext) {
String nodeName = ''; // String nodeName = '';
return InkWell( // return InkWell(
onTap: () { // onTap: () {
showDialog( // showDialog(
context: blocContext, // context: blocContext,
builder: (BuildContext context) { // builder: (BuildContext context) {
return AlertDialog( // return AlertDialog(
title: const Text('Add a child'), // title: const Text('Add a child'),
content: TextField( // content: TextField(
decoration: // decoration:
const InputDecoration(hintText: 'Enter your text here'), // const InputDecoration(hintText: 'Enter your text here'),
onChanged: (value) { // onChanged: (value) {
nodeName = value; // nodeName = value;
}, // },
), // ),
actions: <Widget>[ // actions: <Widget>[
TextButton( // TextButton(
onPressed: () { // onPressed: () {
Navigator.of(context).pop(); // Navigator.of(context).pop();
}, // },
child: Text('Close'), // child: Text('Close'),
), // ),
TextButton( // TextButton(
onPressed: () { // onPressed: () {
if (nodeName.isNotEmpty) { // if (nodeName.isNotEmpty) {
final newNode = Node.Id(nodeName); // final newNode = Node.Id(nodeName);
blocContext.read<HomeBloc>().add(CreateNewNode( // blocContext.read<HomeBloc>().add(CreateNewNode(
sourceNode: node, destinationNode: newNode)); // sourceNode: node, destinationNode: newNode));
} // }
Navigator.of(context).pop(); // Navigator.of(context).pop();
}, // },
child: Text('Add'), // child: Text('Add'),
), // ),
], // ],
); // );
}, // },
); // );
}, // },
child: Container( // child: Container(
width: MediaQuery.of(blocContext).size.width * 0.2, // width: MediaQuery.of(blocContext).size.width * 0.2,
margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), // margin: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
padding: EdgeInsets.all(20.0), // padding: EdgeInsets.all(20.0),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: Colors.white, // color: Colors.white,
borderRadius: BorderRadius.circular(10.0), // borderRadius: BorderRadius.circular(10.0),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: Colors.grey.withOpacity(0.5), // color: Colors.grey.withOpacity(0.5),
spreadRadius: 2, // spreadRadius: 2,
blurRadius: 5, // blurRadius: 5,
offset: Offset(0, 3), // changes position of shadow // offset: Offset(0, 3), // changes position of shadow
), // ),
], // ],
), // ),
child: Row( // child: Row(
children: [ // children: [
const SizedBox( // const SizedBox(
child: Icon( // child: Icon(
Icons.location_on, // Icons.location_on,
color: Colors.blue, // color: Colors.blue,
size: 40.0, // size: 40.0,
), // ),
), // ),
const SizedBox(width: 10.0), // const SizedBox(width: 10.0),
SizedBox( // SizedBox(
child: Text( // child: Text(
text, // text,
style: const TextStyle( // style: const TextStyle(
fontSize: 24.0, // fontSize: 24.0,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
), // ),
), // ),
), // ),
const Spacer(), // const Spacer(),
Container( // Container(
child: const Icon( // child: const Icon(
Icons.add_circle_outline, // Icons.add_circle_outline,
color: Colors.grey, // color: Colors.grey,
size: 24.0, // size: 24.0,
), // ),
), // ),
], // ],
), // ),
), // ),
); // );
} // }

View File

@ -219,6 +219,9 @@ class UserSpaceModel {
final double x; final double x;
final double y; final double y;
final String icon; final String icon;
final String communityUuid;
//communityUuid
UserSpaceModel({ UserSpaceModel({
required this.uuid, required this.uuid,
@ -231,6 +234,7 @@ class UserSpaceModel {
required this.x, required this.x,
required this.y, required this.y,
required this.icon, required this.icon,
required this.communityUuid,
}); });
/// Create a [UserSpaceModel] from JSON data /// Create a [UserSpaceModel] from JSON data
@ -246,7 +250,7 @@ class UserSpaceModel {
x: (json['x'] as num).toDouble(), x: (json['x'] as num).toDouble(),
y: (json['y'] as num).toDouble(), y: (json['y'] as num).toDouble(),
icon: json['icon'] as String, icon: json['icon'] as String,
); communityUuid: json['communityUuid'] as String);
} }
/// Convert the [UserSpaceModel] to JSON /// Convert the [UserSpaceModel] to JSON
@ -262,6 +266,7 @@ class UserSpaceModel {
'x': x, 'x': x,
'y': y, 'y': y,
'icon': icon, 'icon': icon,
'communityUuid': communityUuid
}; };
} }
} }

View File

@ -42,7 +42,9 @@ class RolesUserModel {
invitedBy: invitedBy:
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "), json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
phoneNumber: json['phoneNumber'], phoneNumber: json['phoneNumber'],
jobTitle: json['jobTitle'] ?? "-", jobTitle: json['jobTitle'] == null || json['jobTitle'] == " "
? "_"
: json['jobTitle'],
createdDate: json['createdDate'], createdDate: json['createdDate'],
createdTime: json['createdTime'], createdTime: json['createdTime'],
); );

View File

@ -1,5 +1,7 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/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/edit_user_model.dart';
import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.dart'; import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.dart';
@ -7,11 +9,14 @@ 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/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/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/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/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_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/space_mana_api.dart';
import 'package:syncrow_web/services/user_permission.dart'; import 'package:syncrow_web/services/user_permission.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
class UsersBloc extends Bloc<UsersEvent, UsersState> { class UsersBloc extends Bloc<UsersEvent, UsersState> {
UsersBloc() : super(UsersInitial()) { UsersBloc() : super(UsersInitial()) {
@ -61,8 +66,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
void isCompleteSpacesFun( void isCompleteSpacesFun(
CheckSpacesStepStatus event, Emitter<UsersState> emit) { CheckSpacesStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities); var spaceBloc =
isCompleteSpaces = selectedIds.isNotEmpty; NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
isCompleteSpaces = spaceBloc.state.selectedCommunities.isNotEmpty;
emit(ChangeStatusSteps()); emit(ChangeStatusSteps());
} }
@ -74,18 +81,24 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
Future<List<SpaceModel>> _fetchSpacesForCommunity( Future<List<SpaceModel>> _fetchSpacesForCommunity(
String communityUuid) async { String communityUuid) async {
return await CommunitySpaceManagementApi().getSpaceHierarchy(communityUuid); final projectUuid = await ProjectManager.getProjectUUID() ?? '';
return await CommunitySpaceManagementApi()
.getSpaceHierarchy(communityUuid, projectUuid);
} }
List<TreeNode> updatedCommunities = []; List<TreeNode> updatedCommunities = [];
List<TreeNode> spacesNodes = []; List<TreeNode> spacesNodes = [];
List<String> communityIds = [];
_onLoadCommunityAndSpaces( _onLoadCommunityAndSpaces(
LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async { LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async {
try { try {
emit(UsersLoadingState()); emit(UsersLoadingState());
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
List<CommunityModel> communities = List<CommunityModel> communities =
await CommunitySpaceManagementApi().fetchCommunities(); await CommunitySpaceManagementApi().fetchCommunities(projectUuid);
communityIds = communities.map((community) => community.uuid).toList();
updatedCommunities = await Future.wait( updatedCommunities = await Future.wait(
communities.map((community) async { communities.map((community) async {
List<SpaceModel> spaces = List<SpaceModel> spaces =
@ -101,13 +114,19 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
); );
}).toList(), }).toList(),
); );
originalCommunities = updatedCommunities;
emit(const SpacesLoadedState()); emit(const SpacesLoadedState());
return updatedCommunities;
} catch (e) { } catch (e) {
emit(ErrorState('Error loading communities and spaces: $e')); emit(ErrorState('Error loading communities and spaces: $e'));
} }
} }
// This variable holds the full original list.
List<TreeNode> originalCommunities = [];
// This variable holds the working list that may be filtered.
// Build tree nodes from your data model.
List<TreeNode> _buildTreeNodes(List<SpaceModel> spaces) { List<TreeNode> _buildTreeNodes(List<SpaceModel> spaces) {
return spaces.map((space) { return spaces.map((space) {
List<TreeNode> childNodes = List<TreeNode> childNodes =
@ -123,12 +142,39 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
}).toList(); }).toList();
} }
// Optional helper method to deep clone a TreeNode.
TreeNode _cloneNode(TreeNode node) {
return TreeNode(
uuid: node.uuid,
title: node.title,
isChecked: node.isChecked,
isHighlighted: node.isHighlighted,
isExpanded: node.isExpanded,
children: node.children.map(_cloneNode).toList(),
);
}
// Clone an entire list of tree nodes.
List<TreeNode> _cloneNodes(List<TreeNode> nodes) {
return nodes.map(_cloneNode).toList();
}
// Your search event handler.
void searchTreeNode(SearchAnode event, Emitter<UsersState> emit) { void searchTreeNode(SearchAnode event, Emitter<UsersState> emit) {
emit(UsersLoadingState()); emit(UsersLoadingState());
// If the search term is empty, restore the original list.
if (event.searchTerm!.isEmpty) { if (event.searchTerm!.isEmpty) {
// Clear any highlights on the restored copy.
updatedCommunities = _cloneNodes(originalCommunities);
_clearHighlights(updatedCommunities); _clearHighlights(updatedCommunities);
} else { } else {
_searchAndHighlightNodes(updatedCommunities, event.searchTerm!); // Start with a fresh clone of the original tree.
List<TreeNode> freshClone = _cloneNodes(originalCommunities);
_searchAndHighlightNodes(freshClone, event.searchTerm!);
updatedCommunities = _filterNodes(freshClone, event.searchTerm!);
} }
emit(ChangeStatusSteps()); emit(ChangeStatusSteps());
} }
@ -155,6 +201,91 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
return anyMatch; return anyMatch;
} }
List<TreeNode> _filterNodes(List<TreeNode> nodes, String searchTerm) {
List<TreeNode> filteredNodes = [];
for (var node in nodes) {
bool isMatch =
node.title.toLowerCase().contains(searchTerm.toLowerCase());
List<TreeNode> filteredChildren = _filterNodes(node.children, searchTerm);
if (isMatch || filteredChildren.isNotEmpty) {
node.isHighlighted = isMatch;
node.children = filteredChildren;
filteredNodes.add(node);
}
}
return filteredNodes;
}
// List<TreeNode> _buildTreeNodes(List<SpaceModel> spaces) {
// return spaces.map((space) {
// List<TreeNode> childNodes =
// space.children.isNotEmpty ? _buildTreeNodes(space.children) : [];
// return TreeNode(
// uuid: space.uuid!,
// title: space.name,
// isChecked: false,
// isHighlighted: false,
// isExpanded: childNodes.isNotEmpty,
// children: childNodes,
// );
// }).toList();
// }
// void searchTreeNode(SearchAnode event, Emitter<UsersState> emit) {
// emit(UsersLoadingState());
// if (event.searchTerm!.isEmpty) {
// _clearHighlights(updatedCommunities);
// } else {
// _searchAndHighlightNodes(updatedCommunities, event.searchTerm!);
// updatedCommunities = _filterNodes(updatedCommunities, event.searchTerm!);
// }
// emit(ChangeStatusSteps());
// }
// void _clearHighlights(List<TreeNode> nodes) {
// for (var node in nodes) {
// node.isHighlighted = false;
// if (node.children.isNotEmpty) {
// _clearHighlights(node.children);
// }
// }
// }
// bool _searchAndHighlightNodes(List<TreeNode> nodes, String searchTerm) {
// bool anyMatch = false;
// for (var node in nodes) {
// bool isMatch =
// node.title.toLowerCase().contains(searchTerm.toLowerCase());
// bool childMatch = _searchAndHighlightNodes(node.children, searchTerm);
// node.isHighlighted = isMatch || childMatch;
// anyMatch = anyMatch || node.isHighlighted;
// }
// return anyMatch;
// }
// List<TreeNode> _filterNodes(List<TreeNode> nodes, String searchTerm) {
// List<TreeNode> filteredNodes = [];
// for (var node in nodes) {
// // Check if the current node's title contains the search term.
// bool isMatch =
// node.title.toLowerCase().contains(searchTerm.toLowerCase());
// // Recursively filter the children.
// List<TreeNode> filteredChildren = _filterNodes(node.children, searchTerm);
// // If the current node is a match or any of its children are, include it.
// if (isMatch || filteredChildren.isNotEmpty) {
// // Optionally, update any properties (like isHighlighted) if you still need them.
// node.isHighlighted = isMatch;
// // Replace the children with the filtered ones.
// node.children = filteredChildren;
// filteredNodes.add(node);
// }
// }
// return filteredNodes;
// }
List<String> selectedIds = []; List<String> selectedIds = [];
List<String> getSelectedIds(List<TreeNode> nodes) { List<String> getSelectedIds(List<TreeNode> nodes) {
@ -177,7 +308,6 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
try { try {
emit(UsersLoadingState()); emit(UsersLoadingState());
roles = await UserPermissionApi().fetchRoles(); roles = await UserPermissionApi().fetchRoles();
// add(PermissionEvent(roleUuid: roles.first.uuid));
emit(RolePermissionInitial()); emit(RolePermissionInitial());
} catch (e) { } catch (e) {
emit(ErrorState('Error loading communities and spaces: $e')); emit(ErrorState('Error loading communities and spaces: $e'));
@ -208,10 +338,17 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
return anyMatch; return anyMatch;
} }
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async { void _sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
try { try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities); // 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( bool res = await UserPermissionApi().sendInviteUser(
email: emailController.text, email: emailController.text,
firstName: firstNameController.text, firstName: firstNameController.text,
@ -219,9 +356,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text, lastName: lastNameController.text,
phoneNumber: phoneController.text, phoneNumber: phoneController.text,
roleUuid: roleSelected, roleUuid: roleSelected,
spaceUuids: selectedIds, spaceUuids: selectedSpacesId,
); projectUuid: projectUuid);
if (res == true) {
if (res) {
showCustomDialog( showCustomDialog(
barrierDismissible: false, barrierDismissible: false,
context: event.context, context: event.context,
@ -248,10 +386,22 @@ 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 { _editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
try { try {
emit(UsersLoadingState()); emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities); final projectUuid = await ProjectManager.getProjectUUID() ?? '';
bool res = await UserPermissionApi().editInviteUser( bool res = await UserPermissionApi().editInviteUser(
userId: event.userId, userId: event.userId,
firstName: firstNameController.text, firstName: firstNameController.text,
@ -259,8 +409,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastName: lastNameController.text, lastName: lastNameController.text,
phoneNumber: phoneController.text, phoneNumber: phoneController.text,
roleUuid: roleSelected, roleUuid: roleSelected,
spaceUuids: selectedIds, spaceUuids: getSelectedSpacesIds(),
); projectUuid: projectUuid);
if (res == true) { if (res == true) {
showCustomDialog( showCustomDialog(
barrierDismissible: false, barrierDismissible: false,
@ -365,8 +515,13 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
emit(UsersLoadingState()); emit(UsersLoadingState());
try { try {
var spaceBloc =
NavigationService.navigatorKey.currentContext!.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (event.uuid?.isNotEmpty ?? false) { if (event.uuid?.isNotEmpty ?? false) {
final res = await UserPermissionApi().fetchUserById(event.uuid); final res =
await UserPermissionApi().fetchUserById(event.uuid, projectUuid);
if (res != null) { if (res != null) {
// Populate the text controllers // Populate the text controllers
@ -376,13 +531,20 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
phoneController.text = res.phoneNumber ?? ''; phoneController.text = res.phoneNumber ?? '';
jobTitleController.text = res.jobTitle ?? ''; jobTitleController.text = res.jobTitle ?? '';
res.roleType; res.roleType;
if (updatedCommunities.isNotEmpty) { res.spaces.map((space) {
// Create a list of UUIDs to mark selectedIds.add(space.uuid);
final uuidsToMark = res.spaces.map((space) => space.uuid).toList(); CommunityModel community = spaceBloc.state.communityList
// Print all IDs and mark nodes in updatedCommunities .firstWhere((item) => item.uuid == space.communityUuid);
debugPrint('Printing and marking nodes in updatedCommunities:'); spaceBloc.add(OnSpaceSelected(community, space.uuid, []));
_printAndMarkNodes(updatedCommunities, uuidsToMark); }).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 final roleId = roles
.firstWhere((element) => .firstWhere((element) =>
element.type == element.type ==
@ -475,4 +637,16 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
} }
return null; 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

@ -24,7 +24,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => UsersBloc() create: (BuildContext context) => UsersBloc()
..add(const LoadCommunityAndSpacesEvent()) // ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()), ..add(const RoleEvent()),
child: BlocConsumer<UsersBloc, UsersState>( child: BlocConsumer<UsersBloc, UsersState>(
listener: (context, state) {}, listener: (context, state) {},
@ -34,8 +34,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
return Dialog( return Dialog(
child: Container( child: Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
borderRadius: BorderRadius.all(Radius.circular(20))),
width: 900, width: 900,
child: Column( child: Column(
children: [ children: [
@ -64,8 +63,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
children: [ children: [
_buildStep1Indicator(1, "Basics", _blocRole), _buildStep1Indicator(1, "Basics", _blocRole),
_buildStep2Indicator(2, "Spaces", _blocRole), _buildStep2Indicator(2, "Spaces", _blocRole),
_buildStep3Indicator( _buildStep3Indicator(3, "Role & Permissions", _blocRole),
3, "Role & Permissions", _blocRole),
], ],
), ),
), ),
@ -113,15 +111,12 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
if (currentStep < 3) { if (currentStep < 3) {
currentStep++; currentStep++;
if (currentStep == 2) { if (currentStep == 2) {
_blocRole.add( _blocRole.add(const CheckStepStatus(isEditUser: false));
const CheckStepStatus(isEditUser: false));
} else if (currentStep == 3) { } else if (currentStep == 3) {
_blocRole _blocRole.add(const CheckSpacesStepStatus());
.add(const CheckSpacesStepStatus());
} }
} else { } else {
_blocRole _blocRole.add(SendInviteUsers(context: context));
.add(SendInviteUsers(context: context));
} }
}); });
}, },
@ -129,11 +124,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
currentStep < 3 ? "Next" : "Save", currentStep < 3 ? "Next" : "Save",
style: TextStyle( style: TextStyle(
color: (_blocRole.isCompleteSpaces == false || color: (_blocRole.isCompleteSpaces == false ||
_blocRole.isCompleteBasics == _blocRole.isCompleteBasics == false ||
false || _blocRole.isCompleteRolePermissions == false) &&
_blocRole
.isCompleteRolePermissions ==
false) &&
currentStep == 3 currentStep == 3
? ColorsManager.grayColor ? ColorsManager.grayColor
: ColorsManager.secondaryColor), : ColorsManager.secondaryColor),
@ -204,12 +196,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -236,12 +224,16 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
currentStep = step;
bloc.add(const CheckStepStatus(isEditUser: false)); bloc.add(const CheckStepStatus(isEditUser: false));
currentStep = step;
Future.delayed(const Duration(milliseconds: 500), () {
bloc.add(const CheckStepStatus(isEditUser: false));
});
if (step3 == 3) { if (step3 == 3) {
Future.delayed(const Duration(seconds: 1), () {
bloc.add(const CheckRoleStepStatus()); bloc.add(const CheckRoleStepStatus());
});
} }
}); });
}, },
child: Column( child: Column(
@ -268,12 +260,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -330,12 +318,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],

View File

@ -46,8 +46,9 @@ class BasicsView extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
SizedBox( Flexible(
width: MediaQuery.of(context).size.width * 0.18, child: SizedBox(
// width: MediaQuery.of(context).size.width * 0.18,
height: MediaQuery.of(context).size.width * 0.08, height: MediaQuery.of(context).size.width * 0.08,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -75,8 +76,8 @@ class BasicsView extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: TextFormField( child: TextFormField(
style: style: const TextStyle(
const TextStyle(color: ColorsManager.blackColor), color: ColorsManager.blackColor),
// onChanged: (value) { // onChanged: (value) {
// Future.delayed(const Duration(milliseconds: 200), // Future.delayed(const Duration(milliseconds: 200),
// () { // () {
@ -103,9 +104,11 @@ class BasicsView extends StatelessWidget {
], ],
), ),
), ),
),
const SizedBox(width: 10), const SizedBox(width: 10),
SizedBox( Flexible(
width: MediaQuery.of(context).size.width * 0.18, child: SizedBox(
// width: MediaQuery.of(context).size.width * 0.18,
height: MediaQuery.of(context).size.width * 0.08, height: MediaQuery.of(context).size.width * 0.08,
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -142,8 +145,7 @@ class BasicsView extends StatelessWidget {
decoration: decoration:
inputTextFormDeco(hintText: "Enter last name") inputTextFormDeco(hintText: "Enter last name")
.copyWith( .copyWith(
hintStyle: context hintStyle: context.textTheme.bodyMedium
.textTheme.bodyMedium
?.copyWith( ?.copyWith(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 12, fontSize: 12,
@ -159,6 +161,7 @@ class BasicsView extends StatelessWidget {
], ],
), ),
), ),
),
], ],
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
@ -218,7 +221,7 @@ class BasicsView extends StatelessWidget {
if (_blocRole.checkEmailValid != "Valid email") { if (_blocRole.checkEmailValid != "Valid email") {
return _blocRole.checkEmailValid; return _blocRole.checkEmailValid;
} }
return null; // return null;
}, },
), ),
), ),

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.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_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_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/bloc/users_status.dart';

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.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_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_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/bloc/users_status.dart';
@ -25,13 +26,12 @@ class _EditUserDialogState extends State<EditUserDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => UsersBloc() create: (BuildContext context) => UsersBloc()
..add(const LoadCommunityAndSpacesEvent()) // ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()) ..add(const RoleEvent())
..add(GetUserByIdEvent(uuid: widget.userId)), ..add(GetUserByIdEvent(uuid: widget.userId)),
child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) { child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) {
if (state is SpacesLoadedState) { if (state is SpacesLoadedState) {
BlocProvider.of<UsersBloc>(context) BlocProvider.of<UsersBloc>(context).add(GetUserByIdEvent(uuid: widget.userId));
.add(GetUserByIdEvent(uuid: widget.userId));
} }
}, builder: (context, state) { }, builder: (context, state) {
final _blocRole = BlocProvider.of<UsersBloc>(context); final _blocRole = BlocProvider.of<UsersBloc>(context);
@ -39,8 +39,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
return Dialog( return Dialog(
child: Container( child: Container(
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Colors.white, color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(20))),
borderRadius: BorderRadius.all(Radius.circular(20))),
width: 900, width: 900,
child: Column( child: Column(
children: [ children: [
@ -69,8 +68,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
children: [ children: [
_buildStep1Indicator(1, "Basics", _blocRole), _buildStep1Indicator(1, "Basics", _blocRole),
_buildStep2Indicator(2, "Spaces", _blocRole), _buildStep2Indicator(2, "Spaces", _blocRole),
_buildStep3Indicator( _buildStep3Indicator(3, "Role & Permissions", _blocRole),
3, "Role & Permissions", _blocRole),
], ],
), ),
), ),
@ -118,15 +116,13 @@ class _EditUserDialogState extends State<EditUserDialog> {
if (currentStep < 3) { if (currentStep < 3) {
currentStep++; currentStep++;
if (currentStep == 2) { if (currentStep == 2) {
_blocRole _blocRole.add(CheckStepStatus(isEditUser: true));
.add(CheckStepStatus(isEditUser: true));
} else if (currentStep == 3) { } else if (currentStep == 3) {
_blocRole.add(const CheckSpacesStepStatus()); _blocRole.add(const CheckSpacesStepStatus());
} }
} else { } else {
_blocRole.add(EditInviteUsers( _blocRole
context: context, .add(EditInviteUsers(context: context, userId: widget.userId!));
userId: widget.userId!));
} }
}); });
}, },
@ -135,8 +131,7 @@ class _EditUserDialogState extends State<EditUserDialog> {
style: TextStyle( style: TextStyle(
color: (_blocRole.isCompleteSpaces == false || color: (_blocRole.isCompleteSpaces == false ||
_blocRole.isCompleteBasics == false || _blocRole.isCompleteBasics == false ||
_blocRole.isCompleteRolePermissions == _blocRole.isCompleteRolePermissions == false) &&
false) &&
currentStep == 3 currentStep == 3
? ColorsManager.grayColor ? ColorsManager.grayColor
: ColorsManager.secondaryColor), : ColorsManager.secondaryColor),
@ -209,12 +204,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -272,12 +263,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],
@ -334,12 +321,8 @@ class _EditUserDialogState extends State<EditUserDialog> {
label, label,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
color: currentStep == step color: currentStep == step ? ColorsManager.blackColor : ColorsManager.greyColor,
? ColorsManager.blackColor fontWeight: currentStep == step ? FontWeight.bold : FontWeight.normal,
: ColorsManager.greyColor,
fontWeight: currentStep == step
? FontWeight.bold
: FontWeight.normal,
), ),
), ),
], ],

View File

@ -81,7 +81,7 @@ Future<void> showPopUpFilterMenu({
), ),
const Divider(), const Divider(),
const Text( const Text(
"Filter by Status", "Filter by ",
style: TextStyle(fontWeight: FontWeight.bold), style: TextStyle(fontWeight: FontWeight.bold),
), ),
Container( Container(

View File

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

View File

@ -1,10 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/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_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.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/services/user_permission.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> { class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
UserTableBloc() : super(TableInitial()) { UserTableBloc() : super(TableInitial()) {
@ -27,7 +30,16 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
int currentPage = 1; int currentPage = 1;
List<RolesUserModel> users = []; List<RolesUserModel> users = [];
List<RolesUserModel> initialUsers = []; List<RolesUserModel> initialUsers = [];
List<RolesUserModel> totalUsersCount = [];
String currentSortOrder = ''; String currentSortOrder = '';
String currentSortJopTitle = '';
String currentSortRole = '';
String currentSortCreatedDate = '';
String currentSortStatus = '';
String currentSortCreatedBy = '';
String currentSortOrderDate = ''; String currentSortOrderDate = '';
List<String> roleTypes = []; List<String> roleTypes = [];
List<String> jobTitle = []; List<String> jobTitle = [];
@ -37,12 +49,12 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async { Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async {
emit(UsersLoadingState()); emit(UsersLoadingState());
try { try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
roleTypes.clear(); roleTypes.clear();
jobTitle.clear(); jobTitle.clear();
createdBy.clear(); createdBy.clear();
// deActivate.clear(); users = await UserPermissionApi().fetchUsers(projectUuid);
users = await UserPermissionApi().fetchUsers();
users.sort((a, b) { users.sort((a, b) {
final dateA = _parseDateTime(a.createdDate); final dateA = _parseDateTime(a.createdDate);
final dateB = _parseDateTime(b.createdDate); final dateB = _parseDateTime(b.createdDate);
@ -57,15 +69,13 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
for (var user in users) { for (var user in users) {
createdBy.add(user.invitedBy.toString()); createdBy.add(user.invitedBy.toString());
} }
// for (var user in users) {
// deActivate.add(user.status.toString());
// }
initialUsers = List.from(users); initialUsers = List.from(users);
roleTypes = roleTypes.toSet().toList(); roleTypes = roleTypes.toSet().toList();
jobTitle = jobTitle.toSet().toList(); jobTitle = jobTitle.toSet().toList();
createdBy = createdBy.toSet().toList(); createdBy = createdBy.toSet().toList();
// deActivate = deActivate.toSet().toList();
_handlePageChange(ChangePage(1), emit); _handlePageChange(ChangePage(1), emit);
totalUsersCount = initialUsers;
add(ChangePage(currentPage));
emit(UsersLoadedState(users: users)); emit(UsersLoadedState(users: users));
} catch (e) { } catch (e) {
emit(ErrorState(e.toString())); emit(ErrorState(e.toString()));
@ -91,31 +101,13 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
Future<void> _changeUserStatus( Future<void> _changeUserStatus(
ChangeUserStatus event, Emitter<UserTableState> emit) async { ChangeUserStatus event, Emitter<UserTableState> emit) async {
try { try {
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
emit(UsersLoadingState()); emit(UsersLoadingState());
bool res = await UserPermissionApi().changeUserStatusById( bool res = await UserPermissionApi().changeUserStatusById(event.userId,
event.userId, event.newStatus == "disabled" ? false : true); event.newStatus == "disabled" ? false : true, projectUuid);
if (res == true) { if (res == true) {
add(const GetUsers()); add(const GetUsers());
// users = users.map((user) {
// if (user.uuid == event.userId) {
// return RolesUserModel(
// uuid: user.uuid,
// createdAt: user.createdAt,
// email: user.email,
// firstName: user.firstName,
// lastName: user.lastName,
// roleType: user.roleType,
// status: event.newStatus,
// isEnabled: event.newStatus == "disabled" ? false : true,
// invitedBy: user.invitedBy,
// phoneNumber: user.phoneNumber,
// jobTitle: user.jobTitle,
// createdDate: user.createdDate,
// createdTime: user.createdTime,
// );
// }
// return user;
// }).toList();
} }
emit(UsersLoadedState(users: users)); emit(UsersLoadedState(users: users));
} catch (e) { } catch (e) {
@ -125,11 +117,14 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
void _toggleSortUsersByNameAsc( void _toggleSortUsersByNameAsc(
SortUsersByNameAsc event, Emitter<UserTableState> emit) { SortUsersByNameAsc event, Emitter<UserTableState> emit) {
selectedRoles.clear();
selectedJobTitles.clear();
selectedCreatedBy.clear();
selectedStatuses.clear();
if (currentSortOrder == "Asc") { if (currentSortOrder == "Asc") {
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = ""; currentSortOrder = "";
users = List.from(users); users = List.from(users);
emit(UsersLoadedState(users: users));
} else { } else {
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = "Asc"; currentSortOrder = "Asc";
@ -137,28 +132,42 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
.toString() .toString()
.toLowerCase() .toLowerCase()
.compareTo(b.firstName.toString().toLowerCase())); .compareTo(b.firstName.toString().toLowerCase()));
emit(UsersLoadedState(users: users));
} }
currentSortJopTitle = '';
currentSortCreatedDate = '';
currentSortStatus = '';
currentSortCreatedBy = '';
emit(UsersLoadedState(users: users));
} }
void _toggleSortUsersByNameDesc( void _toggleSortUsersByNameDesc(
SortUsersByNameDesc event, Emitter<UserTableState> emit) { SortUsersByNameDesc event, Emitter<UserTableState> emit) {
selectedRoles.clear();
selectedJobTitles.clear();
selectedCreatedBy.clear();
selectedStatuses.clear();
if (currentSortOrder == "Desc") { if (currentSortOrder == "Desc") {
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = ""; currentSortOrder = "";
users = List.from(initialUsers); // Reset to saved initial state users = List.from(initialUsers);
emit(UsersLoadedState(users: users));
} else { } else {
// Sort descending
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = "Desc"; currentSortOrder = "Desc";
users.sort((a, b) => b.firstName!.compareTo(a.firstName!)); users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
emit(UsersLoadedState(users: users));
} }
currentSortJopTitle = '';
currentSortCreatedDate = '';
currentSortStatus = '';
currentSortCreatedBy = '';
emit(UsersLoadedState(users: users));
} }
void _toggleSortUsersByDateNewestToOldest( void _toggleSortUsersByDateNewestToOldest(
DateNewestToOldestEvent event, Emitter<UserTableState> emit) { DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
selectedRoles.clear();
selectedJobTitles.clear();
selectedCreatedBy.clear();
selectedStatuses.clear();
if (currentSortOrderDate == "NewestToOldest") { if (currentSortOrderDate == "NewestToOldest") {
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = ""; currentSortOrder = "";
@ -179,6 +188,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
void _toggleSortUsersByDateOldestToNewest( void _toggleSortUsersByDateOldestToNewest(
DateOldestToNewestEvent event, Emitter<UserTableState> emit) { DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
selectedRoles.clear();
selectedJobTitles.clear();
selectedCreatedBy.clear();
selectedStatuses.clear();
if (currentSortOrderDate == "OldestToNewest") { if (currentSortOrderDate == "OldestToNewest") {
emit(UsersLoadingState()); emit(UsersLoadingState());
currentSortOrder = ""; currentSortOrder = "";
@ -212,6 +225,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
Future<void> _searchUsers( Future<void> _searchUsers(
SearchUsers event, Emitter<UserTableState> emit) async { SearchUsers event, Emitter<UserTableState> emit) async {
try { try {
emit(TableSearch());
final query = event.query.toLowerCase(); final query = event.query.toLowerCase();
final filteredUsers = initialUsers.where((user) { final filteredUsers = initialUsers.where((user) {
final fullName = "${user.firstName} ${user.lastName}".toLowerCase(); final fullName = "${user.firstName} ${user.lastName}".toLowerCase();
@ -240,7 +254,8 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} }
void _handlePageChange(ChangePage event, Emitter<UserTableState> emit) { void _handlePageChange(ChangePage event, Emitter<UserTableState> emit) {
const itemsPerPage = 10; currentPage = event.pageNumber;
const itemsPerPage = 20;
final startIndex = (event.pageNumber - 1) * itemsPerPage; final startIndex = (event.pageNumber - 1) * itemsPerPage;
final endIndex = startIndex + itemsPerPage; final endIndex = startIndex + itemsPerPage;
if (startIndex >= users.length) { if (startIndex >= users.length) {
@ -277,9 +292,15 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} else if (event.sortOrder == "Desc") { } else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc"; currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!)); filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else { } else {}
currentSortOrder = ""; currentSortOrder = "";
} currentSortCreatedDate = '';
currentSortStatus = '';
currentSortCreatedBy = '';
currentSortJopTitle = '';
currentSortOrderDate = "";
totalUsersCount = filteredUsers;
emit(UsersLoadedState(users: filteredUsers)); emit(UsersLoadedState(users: filteredUsers));
} }
@ -301,9 +322,16 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} else if (event.sortOrder == "Desc") { } else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc"; currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!)); filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else { } else {}
currentSortOrder = ""; currentSortOrder = "";
} currentSortCreatedDate = '';
currentSortStatus = '';
currentSortCreatedBy = '';
currentSortRole = '';
currentSortOrderDate = "";
totalUsersCount = filteredUsers;
emit(UsersLoadedState(users: filteredUsers)); emit(UsersLoadedState(users: filteredUsers));
} }
@ -325,9 +353,15 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} else if (event.sortOrder == "Desc") { } else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc"; currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!)); filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else { } else {}
currentSortOrder = ""; currentSortOrder = '';
} currentSortRole = '';
currentSortCreatedDate = '';
currentSortStatus = '';
currentSortOrderDate = "";
totalUsersCount = filteredUsers;
emit(UsersLoadedState(users: filteredUsers)); emit(UsersLoadedState(users: filteredUsers));
} }
@ -337,7 +371,20 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
final filteredUsers = initialUsers.where((user) { final filteredUsers = initialUsers.where((user) {
if (selectedStatuses.isEmpty) return true; if (selectedStatuses.isEmpty) return true;
return selectedStatuses.contains(user.status);
return selectedStatuses.any((status) {
final userStatus = user.status?.toLowerCase() ?? '';
switch (status.toLowerCase()) {
case 'active':
return user.isEnabled == true && userStatus != 'invited';
case 'disabled':
return user.isEnabled == false;
case 'invited':
return userStatus == 'invited';
default:
return false;
}
});
}).toList(); }).toList();
if (event.sortOrder == "Asc") { if (event.sortOrder == "Asc") {
currentSortOrder = "Asc"; currentSortOrder = "Asc";
@ -348,9 +395,14 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} else if (event.sortOrder == "Desc") { } else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc"; currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!)); filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else { totalUsersCount = filteredUsers;
currentSortOrder = ""; } else {}
} currentSortOrder = '';
currentSortRole = '';
currentSortCreatedDate = '';
currentSortCreatedBy = '';
currentSortOrderDate = "";
emit(UsersLoadedState(users: filteredUsers)); emit(UsersLoadedState(users: filteredUsers));
} }

View File

@ -9,7 +9,10 @@ final class TableInitial extends UserTableState {
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
final class TableSearch extends UserTableState {
@override
List<Object> get props => [];
}
final class RolesLoadingState extends UserTableState { final class RolesLoadingState extends UserTableState {
@override @override
List<Object> get props => []; List<Object> get props => [];

View File

@ -12,7 +12,7 @@ Future<void> showDateFilterMenu({
Overlay.of(context).context.findRenderObject() as RenderBox; Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect( final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTRB( Rect.fromLTRB(
overlay.size.width / 2, overlay.size.width / 3,
240, 240,
0, 0,
overlay.size.height, overlay.size.height,
@ -40,7 +40,6 @@ Future<void> showDateFilterMenu({
), ),
title: Text( title: Text(
"Sort from newest to oldest", "Sort from newest to oldest",
// style: context.textTheme.bodyMedium,
style: TextStyle( style: TextStyle(
color: isSelected == "NewestToOldest" color: isSelected == "NewestToOldest"
? Colors.black ? Colors.black
@ -65,9 +64,5 @@ Future<void> showDateFilterMenu({
), ),
), ),
], ],
).then((value) { ).then((value) {});
// setState(() {
// _isDropdownOpen = false;
// });
});
} }

View File

@ -40,7 +40,6 @@ Future<void> showDeActivateFilterMenu({
), ),
title: Text( title: Text(
"Sort A to Z", "Sort A to Z",
// style: context.textTheme.bodyMedium,
style: TextStyle( style: TextStyle(
color: isSelected == "NewestToOldest" color: isSelected == "NewestToOldest"
? Colors.black ? Colors.black
@ -65,9 +64,5 @@ Future<void> showDeActivateFilterMenu({
), ),
), ),
], ],
).then((value) { ).then((value) {});
// setState(() {
// _isDropdownOpen = false;
// });
});
} }

View File

@ -12,7 +12,7 @@ Future<void> showNameMenu({
Overlay.of(context).context.findRenderObject() as RenderBox; Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect( final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTRB( Rect.fromLTRB(
overlay.size.width / 25, overlay.size.width / 35,
240, 240,
0, 0,
overlay.size.height, overlay.size.height,
@ -40,7 +40,6 @@ Future<void> showNameMenu({
), ),
title: Text( title: Text(
"Sort A to Z", "Sort A to Z",
// style: context.textTheme.bodyMedium,
style: TextStyle( style: TextStyle(
color: isSelected == "Asc" ? Colors.black : Colors.blueGrey), color: isSelected == "Asc" ? Colors.black : Colors.blueGrey),
), ),
@ -61,9 +60,5 @@ Future<void> showNameMenu({
), ),
), ),
], ],
).then((value) { ).then((value) {});
// setState(() {
// _isDropdownOpen = false;
// });
});
} }

View File

@ -1,255 +1,58 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
class DynamicTableScreen extends StatefulWidget { class _HeaderColumn extends StatelessWidget {
final List<String> titles; final String title;
final List<List<Widget>> rows; final double width;
final void Function(int columnIndex)? onFilter; final bool showFilter;
final VoidCallback? onFilter;
final Function(double) onResize;
DynamicTableScreen( const _HeaderColumn({
{required this.titles, required this.rows, required this.onFilter}); required this.title,
required this.width,
@override required this.showFilter,
_DynamicTableScreenState createState() => _DynamicTableScreenState(); required this.onResize,
} this.onFilter,
Key? key,
class _DynamicTableScreenState extends State<DynamicTableScreen> }) : super(key: key);
with WidgetsBindingObserver {
late List<double> columnWidths;
late double totalWidth;
@override
void initState() {
super.initState();
columnWidths = List<double>.filled(widget.titles.length, 150.0);
totalWidth = columnWidths.reduce((a, b) => a + b);
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
final newScreenWidth = MediaQuery.of(context).size.width;
setState(() {
columnWidths = List<double>.generate(widget.titles.length, (index) {
if (index == 1) {
return newScreenWidth *
0.12; // 20% of screen width for the second column
} else if (index == 9) {
return newScreenWidth *
0.1; // 25% of screen width for the tenth column
}
return newScreenWidth *
0.09; // Default to 10% of screen width for other columns
});
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width; return MouseRegion(
if (columnWidths.every((width) => width == screenWidth * 7)) { cursor: SystemMouseCursors.resizeColumn,
columnWidths = List<double>.generate(widget.titles.length, (index) { child: GestureDetector(
if (index == 1) { onHorizontalDragUpdate: (details) => onResize(details.delta.dx),
return screenWidth * 0.11;
} else if (index == 9) {
return screenWidth * 0.1;
}
return screenWidth * 0.09;
});
setState(() {});
}
return SingleChildScrollView(
clipBehavior: Clip.none,
scrollDirection: Axis.horizontal,
child: Container( child: Container(
decoration: containerDecoration.copyWith( width: width,
color: ColorsManager.whiteColors, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
borderRadius: const BorderRadius.all(Radius.circular(20))), decoration: const BoxDecoration(
child: FittedBox( border: Border(right: BorderSide(color: ColorsManager.boxDivider)),
child: Column( ),
children: [
Container(
width: totalWidth,
decoration: containerDecoration.copyWith(
color: ColorsManager.circleRolesBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15))),
child: Row(
children: List.generate(widget.titles.length, (index) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
FittedBox(
child: Container(
padding: const EdgeInsets.only(left: 5, right: 5),
width: columnWidths[index],
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox( Expanded(
child: Text( child: Text(
widget.titles[index], title,
maxLines: 2, maxLines: 2,
style: const TextStyle(
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 13, fontSize: 13,
color: ColorsManager.grayColor, color: ColorsManager.grayColor,
), ),
), ),
), ),
if (index != 1 && if (showFilter)
index != 9 && IconButton(
index != 8 && icon: SvgPicture.asset(Assets.filterTableIcon),
index != 5) onPressed: onFilter,
FittedBox( padding: EdgeInsets.zero,
child: IconButton( constraints: const BoxConstraints(),
icon: SvgPicture.asset(
Assets.filterTableIcon,
fit: BoxFit.none,
),
onPressed: () {
if (widget.onFilter != null) {
widget.onFilter!(index);
}
},
),
)
],
),
),
),
GestureDetector(
onHorizontalDragUpdate: (details) {
setState(() {
columnWidths[index] =
(columnWidths[index] + details.delta.dx)
.clamp(150.0, 300.0);
totalWidth = columnWidths.reduce((a, b) => a + b);
});
},
child: MouseRegion(
cursor: SystemMouseCursors.resizeColumn,
child: Container(
color: Colors.green,
child: Container(
color: ColorsManager.boxDivider,
width: 1,
height: 50,
),
),
),
),
],
);
}),
),
),
widget.rows.isEmpty
? SizedBox(
height: MediaQuery.of(context).size.height / 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SvgPicture.asset(Assets.emptyTable),
const SizedBox(
height: 15,
),
const Text(
'No Users',
style: TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 16,
fontWeight: FontWeight.w700),
)
],
),
],
),
)
: Center(
child: Container(
width: totalWidth,
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15))),
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.rows.length,
itemBuilder: (context, rowIndex) {
if (columnWidths.every((width) => width == 120.0)) {
columnWidths = List<double>.generate(
widget.titles.length, (index) {
if (index == 1) {
return screenWidth * 0.11;
} else if (index == 9) {
return screenWidth * 0.2;
}
return screenWidth * 0.11;
});
setState(() {});
}
final row = widget.rows[rowIndex];
return Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(
left: 5, top: 10, right: 5, bottom: 10),
child: Row(
children:
List.generate(row.length, (index) {
return SizedBox(
width: columnWidths[index],
child: SizedBox(
child: Padding(
padding: const EdgeInsets.only(
left: 15, right: 10),
child: row[index],
),
),
);
}),
),
),
),
if (rowIndex < widget.rows.length - 1)
Row(
children: List.generate(
widget.titles.length, (index) {
return SizedBox(
width: columnWidths[index],
child: const Divider(
color: ColorsManager.boxDivider,
thickness: 1,
height: 1,
),
);
}),
),
],
);
},
),
),
), ),
], ],
), ),
@ -258,3 +61,204 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
); );
} }
} }
class _TableRow extends StatelessWidget {
final List<Widget> cells;
final List<double> columnWidths;
final bool isLast;
const _TableRow({
required this.cells,
required this.columnWidths,
required this.isLast,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
children: [
for (int i = 0; i < cells.length; i++)
Container(
width: columnWidths[i],
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
// decoration: BoxDecoration(
// border: Border(
// right: BorderSide(color: ColorsManager.boxDivider),
// ),
// ),
child: cells[i],
),
],
),
if (!isLast)
Divider(
height: 1,
thickness: 1,
color: ColorsManager.boxDivider,
),
],
);
}
}
//===========================================================================
class DynamicTableScreen extends StatefulWidget {
final List<String> titles;
final List<List<Widget>> rows;
final void Function(int columnIndex)? onFilter;
const DynamicTableScreen({
required this.titles,
required this.rows,
required this.onFilter,
Key? key,
}) : super(key: key);
@override
_DynamicTableScreenState createState() => _DynamicTableScreenState();
}
class _DynamicTableScreenState extends State<DynamicTableScreen> {
late List<double> columnWidths;
final double _minColumnWidth = 100.0;
final double _maxColumnWidth = 300.0;
final double _dividerWidth = 1.0;
double _lastAvailableWidth = 0;
@override
void initState() {
super.initState();
columnWidths = List.filled(widget.titles.length, _minColumnWidth);
}
void _handleColumnResize(int index, double delta) {
setState(() {
double newWidth = columnWidths[index] + delta;
newWidth = newWidth.clamp(_minColumnWidth, _maxColumnWidth);
double actualDelta = newWidth - columnWidths[index];
if (actualDelta == 0) return;
int nextIndex = (index + 1) % columnWidths.length;
columnWidths[index] = newWidth;
columnWidths[nextIndex] = (columnWidths[nextIndex] - actualDelta)
.clamp(_minColumnWidth, _maxColumnWidth);
});
}
Widget _buildHeader() {
return Container(
decoration: containerDecoration.copyWith(
color: ColorsManager.circleRolesBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
),
child: Row(
children: [
for (int i = 0; i < widget.titles.length; i++)
_HeaderColumn(
title: widget.titles[i],
width: columnWidths[i],
showFilter: i != 1 && i != 9 && i != 8 && i != 5,
onFilter: () => widget.onFilter?.call(i),
onResize: (delta) => _handleColumnResize(i, delta),
),
],
),
);
}
Widget _buildBody() {
if (widget.rows.isEmpty) {
return SizedBox(
height: 300,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(Assets.emptyTable),
const SizedBox(height: 15),
const Text(
'No Users',
style: TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
],
),
),
);
}
return Container(
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15),
),
),
child: Column(
children: [
for (int rowIndex = 0; rowIndex < widget.rows.length; rowIndex++)
_TableRow(
cells: widget.rows[rowIndex],
columnWidths: columnWidths,
isLast: rowIndex == widget.rows.length - 1,
),
],
),
);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final availableWidth = constraints.maxWidth;
final totalDividersWidth = (widget.titles.length - 1) * _dividerWidth;
if (_lastAvailableWidth != availableWidth) {
final equalWidth =
(availableWidth - totalDividersWidth) / widget.titles.length;
final clampedWidth =
equalWidth.clamp(_minColumnWidth, _maxColumnWidth);
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
columnWidths = List.filled(widget.titles.length, clampedWidth);
_lastAvailableWidth = availableWidth;
});
});
}
final totalTableWidth =
columnWidths.fold(0.0, (sum, w) => sum + w) + totalDividersWidth;
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: totalTableWidth,
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.all(Radius.circular(20)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
_buildBody(),
],
),
),
);
},
);
}
}

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/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/name_filter.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/user_table.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/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -25,7 +27,7 @@ class UsersPage extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
Widget actionButton({required String title, required Function()? onTap}) { Widget actionButton({bool isActive = false, required String title, Function()? onTap}) {
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: Padding( child: Padding(
@ -33,7 +35,9 @@ class UsersPage extends StatelessWidget {
child: Text( child: Text(
title, title,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: title == "Delete" color: isActive == false && title != "Delete"
? Colors.grey
: title == "Delete"
? ColorsManager.red ? ColorsManager.red
: ColorsManager.spaceColor, : ColorsManager.spaceColor,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -56,8 +60,7 @@ class UsersPage extends StatelessWidget {
: ColorsManager.disabledPink.withOpacity(0.5), : ColorsManager.disabledPink.withOpacity(0.5),
), ),
child: Padding( child: Padding(
padding: padding: const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
const EdgeInsets.only(left: 10, right: 10, bottom: 5, top: 5),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -81,15 +84,12 @@ class UsersPage extends StatelessWidget {
} }
Widget changeIconStatus( Widget changeIconStatus(
{required String userId, {required String userId, required String status, required Function()? onTap}) {
required String status,
required Function()? onTap}) {
return Center( return Center(
child: InkWell( child: InkWell(
onTap: onTap, onTap: onTap,
child: Padding( child: Padding(
padding: padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
child: SvgPicture.asset( child: SvgPicture.asset(
status == "invited" status == "invited"
? Assets.invitedIcon ? Assets.invitedIcon
@ -108,7 +108,6 @@ class UsersPage extends StatelessWidget {
final screenSize = MediaQuery.of(context).size; final screenSize = MediaQuery.of(context).size;
final _blocRole = BlocProvider.of<UserTableBloc>(context); final _blocRole = BlocProvider.of<UserTableBloc>(context);
if (state is UsersLoadingState) { if (state is UsersLoadingState) {
_blocRole.add(ChangePage(_blocRole.currentPage));
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} else if (state is UsersLoadedState) { } else if (state is UsersLoadedState) {
return Padding( return Padding(
@ -130,9 +129,12 @@ class UsersPage extends StatelessWidget {
child: TextFormField( child: TextFormField(
controller: searchController, controller: searchController,
onChanged: (value) { onChanged: (value) {
context final bloc = context.read<UserTableBloc>();
.read<UserTableBloc>() bloc.add(FilterClearEvent());
.add(SearchUsers(value)); bloc.add(SearchUsers(value));
if (value == '') {
bloc.add(ChangePage(1));
}
}, },
style: const TextStyle(color: Colors.black), style: const TextStyle(color: Colors.black),
decoration: textBoxDecoration(radios: 15)!.copyWith( decoration: textBoxDecoration(radios: 15)!.copyWith(
@ -155,6 +157,7 @@ class UsersPage extends StatelessWidget {
const SizedBox(width: 20), const SizedBox(width: 20),
InkWell( InkWell(
onTap: () { onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
@ -193,14 +196,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortOrder,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const SortUsersByNameAsc());
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const SortUsersByNameDesc());
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
}, },
); );
} }
@ -209,13 +208,12 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.jobTitle) for (var item in _blocRole.jobTitle)
item: _blocRole.selectedJobTitles.contains(item), item: _blocRole.selectedJobTitles.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 4, overlay.size.width / 5.3,
240, 240,
overlay.size.width / 4, overlay.size.width / 4,
0, 0,
@ -223,8 +221,9 @@ class UsersPage extends StatelessWidget {
list: _blocRole.jobTitle, list: _blocRole.jobTitle,
context: context, context: context,
checkboxStates: checkboxStates, checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortJopTitle,
onOkPressed: () { onOkPressed: () {
searchController.clear();
_blocRole.add(FilterClearEvent()); _blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries final selectedItems = checkboxStates.entries
.where((entry) => entry.value) .where((entry) => entry.value)
@ -233,14 +232,14 @@ class UsersPage extends StatelessWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
_blocRole.add(FilterUsersByJobEvent( _blocRole.add(FilterUsersByJobEvent(
selectedJob: selectedItems, selectedJob: selectedItems,
sortOrder: _blocRole.currentSortOrder, sortOrder: _blocRole.currentSortJopTitle,
)); ));
}, },
onSortAtoZ: (v) { onSortAtoZ: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortJopTitle = v;
}, },
onSortZtoA: (v) { onSortZtoA: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortJopTitle = v;
}, },
); );
} }
@ -250,9 +249,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.roleTypes) for (var item in _blocRole.roleTypes)
item: _blocRole.selectedRoles.contains(item), item: _blocRole.selectedRoles.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 4, overlay.size.width / 4,
@ -263,24 +261,24 @@ class UsersPage extends StatelessWidget {
list: _blocRole.roleTypes, list: _blocRole.roleTypes,
context: context, context: context,
checkboxStates: checkboxStates, checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortRole,
onOkPressed: () { onOkPressed: () {
searchController.clear();
_blocRole.add(FilterClearEvent()); _blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries final selectedItems = checkboxStates.entries
.where((entry) => entry.value) .where((entry) => entry.value)
.map((entry) => entry.key) .map((entry) => entry.key)
.toList(); .toList();
Navigator.of(context).pop(); Navigator.of(context).pop();
context.read<UserTableBloc>().add( context.read<UserTableBloc>().add(FilterUsersByRoleEvent(
FilterUsersByRoleEvent(
selectedRoles: selectedItems, selectedRoles: selectedItems,
sortOrder: _blocRole.currentSortOrder)); sortOrder: _blocRole.currentSortRole));
}, },
onSortAtoZ: (v) { onSortAtoZ: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortRole = v;
}, },
onSortZtoA: (v) { onSortZtoA: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortRole = v;
}, },
); );
} }
@ -289,14 +287,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortOrder,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
}, },
); );
} }
@ -305,9 +299,8 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.createdBy) for (var item in _blocRole.createdBy)
item: _blocRole.selectedCreatedBy.contains(item), item: _blocRole.selectedCreatedBy.contains(item),
}; };
final RenderBox overlay = Overlay.of(context) final RenderBox overlay =
.context Overlay.of(context).context.findRenderObject() as RenderBox;
.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 1, overlay.size.width / 1,
@ -318,8 +311,9 @@ class UsersPage extends StatelessWidget {
list: _blocRole.createdBy, list: _blocRole.createdBy,
context: context, context: context,
checkboxStates: checkboxStates, checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortCreatedBy,
onOkPressed: () { onOkPressed: () {
searchController.clear();
_blocRole.add(FilterClearEvent()); _blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries final selectedItems = checkboxStates.entries
.where((entry) => entry.value) .where((entry) => entry.value)
@ -328,13 +322,13 @@ class UsersPage extends StatelessWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
_blocRole.add(FilterUsersByCreatedEvent( _blocRole.add(FilterUsersByCreatedEvent(
selectedCreatedBy: selectedItems, selectedCreatedBy: selectedItems,
sortOrder: _blocRole.currentSortOrder)); sortOrder: _blocRole.currentSortCreatedBy));
}, },
onSortAtoZ: (v) { onSortAtoZ: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortCreatedBy = v;
}, },
onSortZtoA: (v) { onSortZtoA: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortCreatedBy = v;
}, },
); );
} }
@ -343,23 +337,23 @@ class UsersPage extends StatelessWidget {
for (var item in _blocRole.status) for (var item in _blocRole.status)
item: _blocRole.selectedStatuses.contains(item), item: _blocRole.selectedStatuses.contains(item),
}; };
final RenderBox overlay = Overlay.of(context)
.context final RenderBox overlay =
.findRenderObject() as RenderBox; Overlay.of(context).context.findRenderObject() as RenderBox;
showPopUpFilterMenu( showPopUpFilterMenu(
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
overlay.size.width / 0, overlay.size.width / 0,
240, 240,
overlay.size.width / 4, overlay.size.width / 5,
0, 0,
), ),
list: _blocRole.status, list: _blocRole.status,
context: context, context: context,
checkboxStates: checkboxStates, checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder, isSelected: _blocRole.currentSortStatus,
onOkPressed: () { onOkPressed: () {
searchController.clear();
_blocRole.add(FilterClearEvent()); _blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries final selectedItems = checkboxStates.entries
.where((entry) => entry.value) .where((entry) => entry.value)
.map((entry) => entry.key) .map((entry) => entry.key)
@ -367,13 +361,13 @@ class UsersPage extends StatelessWidget {
Navigator.of(context).pop(); Navigator.of(context).pop();
_blocRole.add(FilterUsersByDeActevateEvent( _blocRole.add(FilterUsersByDeActevateEvent(
selectedActivate: selectedItems, selectedActivate: selectedItems,
sortOrder: _blocRole.currentSortOrder)); sortOrder: _blocRole.currentSortStatus));
}, },
onSortAtoZ: (v) { onSortAtoZ: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortStatus = v;
}, },
onSortZtoA: (v) { onSortZtoA: (v) {
_blocRole.currentSortOrder = v; _blocRole.currentSortStatus = v;
}, },
); );
} }
@ -382,14 +376,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
isSelected: _blocRole.currentSortOrderDate, isSelected: _blocRole.currentSortOrderDate,
aToZTap: () { aToZTap: () {
context context.read<UserTableBloc>().add(const DateNewestToOldestEvent());
.read<UserTableBloc>()
.add(const DateNewestToOldestEvent());
}, },
zToaTap: () { zToaTap: () {
context context.read<UserTableBloc>().add(const DateOldestToNewestEvent());
.read<UserTableBloc>()
.add(const DateOldestToNewestEvent());
}, },
); );
} }
@ -410,46 +400,34 @@ class UsersPage extends StatelessWidget {
return [ return [
Text('${user.firstName} ${user.lastName}'), Text('${user.firstName} ${user.lastName}'),
Text(user.email), Text(user.email),
Text(user.jobTitle ?? '-'), Text(user.jobTitle),
Text(user.roleType ?? ''), Text(user.roleType ?? ''),
Text(user.createdDate ?? ''), Text(user.createdDate ?? ''),
Text(user.createdTime ?? ''), Text(user.createdTime ?? ''),
Text(user.invitedBy), Text(user.invitedBy),
status( status(
status: user.isEnabled == false status: user.isEnabled == false ? 'disabled' : user.status,
? 'disabled'
: user.status,
), ),
changeIconStatus( changeIconStatus(
status: user.isEnabled == false status: user.isEnabled == false ? 'disabled' : user.status,
? 'disabled'
: user.status,
userId: user.uuid, userId: user.uuid,
onTap: user.status != "invited" onTap: user.status != "invited"
? () { ? () {
// final newStatus = user.status == 'active' context.read<UserTableBloc>().add(ChangeUserStatus(
// ? 'disabled'
// : user.status == 'disabled'
// ? 'invited'
// : 'active';
context.read<UserTableBloc>().add(
ChangeUserStatus(
userId: user.uuid, userId: user.uuid,
newStatus: user.isEnabled == false newStatus:
? 'disabled' user.isEnabled == false ? 'disabled' : user.status));
: user.status));
} }
: null, : null,
), ),
Row( Row(
children: [ children: [
// actionButton( user.isEnabled != false
// title: "Activity Log", ? actionButton(
// onTap: () {}, isActive: true,
// ),
actionButton(
title: "Edit", title: "Edit",
onTap: () { onTap: () {
context.read<SpaceTreeBloc>().add(ClearCachedData());
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
@ -464,6 +442,9 @@ class UsersPage extends StatelessWidget {
} }
}); });
}, },
)
: actionButton(
title: "Edit",
), ),
actionButton( actionButton(
title: "Delete", title: "Delete",
@ -472,13 +453,10 @@ class UsersPage extends StatelessWidget {
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (BuildContext context) { builder: (BuildContext context) {
return DeleteUserDialog( return DeleteUserDialog(onTapDelete: () async {
onTapDelete: () async {
try { try {
_blocRole.add(DeleteUserEvent( _blocRole.add(DeleteUserEvent(user.uuid, context));
user.uuid, context)); await Future.delayed(const Duration(seconds: 2));
await Future.delayed(
const Duration(seconds: 2));
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
@ -486,11 +464,9 @@ class UsersPage extends StatelessWidget {
}); });
}, },
).then((v) { ).then((v) {
if (v != null) {
if (v != null) { if (v != null) {
_blocRole.add(const GetUsers()); _blocRole.add(const GetUsers());
} }
}
}); });
}, },
), ),
@ -510,21 +486,14 @@ class UsersPage extends StatelessWidget {
visiblePagesCount: 4, visiblePagesCount: 4,
buttonRadius: 10, buttonRadius: 10,
selectedButtonColor: ColorsManager.secondaryColor, selectedButtonColor: ColorsManager.secondaryColor,
buttonUnSelectedBorderColor: buttonUnSelectedBorderColor: ColorsManager.grayBorder,
ColorsManager.grayBorder, lastPageIcon: const Icon(Icons.keyboard_double_arrow_right),
lastPageIcon: firstPageIcon: const Icon(Icons.keyboard_double_arrow_left),
const Icon(Icons.keyboard_double_arrow_right), totalPages:
firstPageIcon: (_blocRole.totalUsersCount.length / _blocRole.itemsPerPage).ceil(),
const Icon(Icons.keyboard_double_arrow_left),
totalPages: (_blocRole.users.length /
_blocRole.itemsPerPage)
.ceil(),
currentPage: _blocRole.currentPage, currentPage: _blocRole.currentPage,
onPageChanged: (int pageNumber) { onPageChanged: (int pageNumber) {
_blocRole.currentPage = pageNumber; context.read<UserTableBloc>().add(ChangePage(pageNumber));
context
.read<UserTableBloc>()
.add(ChangePage(pageNumber));
}, },
), ),
), ),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.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/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_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_state.dart'; import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_state.dart';
@ -8,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/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/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.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'; import 'package:syncrow_web/web_layout/web_scaffold.dart';
class RolesAndPermissionPage extends StatelessWidget { class RolesAndPermissionPage extends StatelessWidget {
@ -26,11 +28,10 @@ class RolesAndPermissionPage extends StatelessWidget {
? const Center(child: CircularProgressIndicator()) ? const Center(child: CircularProgressIndicator())
: WebScaffold( : WebScaffold(
enableMenuSidebar: false, enableMenuSidebar: false,
appBarTitle: FittedBox( appBarTitle: Text(
child: Text(
'Roles & Permissions', 'Roles & Permissions',
style: Theme.of(context).textTheme.headlineLarge, style:
), ResponsiveTextTheme.of(context).deviceManagementTitle,
), ),
rightBody: const NavigateHomeGridView(), rightBody: const NavigateHomeGridView(),
centerBody: Row( centerBody: Row(

View File

@ -1,85 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/widgets/routine_dialogs/two_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart';
class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context,
Map<String, dynamic> data, {
required bool removeComparetors,
}) async {
final functions = data['functions'] as List<DeviceFunction>;
try {
final result = await _getDialogForDeviceType(
context,
data['productType'],
data,
functions,
removeComparetors: removeComparetors,
);
if (result != null) {
return result;
}
} catch (e) {
debugPrint('Error: $e');
}
return null;
}
static Future<Map<String, dynamic>?> _getDialogForDeviceType(
BuildContext context,
String productType,
Map<String, dynamic> data,
List<DeviceFunction> functions,
{required bool removeComparetors}) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(
context,
functions,
data['device'],
deviceSelectedFunctions,
data['uniqueCustomId'],
removeComparetors);
default:
return null;
}
}
}

View File

@ -1,69 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/view/create_new_routine_view.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/fetch_routine_scenes_automation.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class RoutinesView extends StatefulWidget {
const RoutinesView({super.key});
@override
State<RoutinesView> createState() => _RoutinesViewState();
}
class _RoutinesViewState extends State<RoutinesView> {
@override
void initState() {
super.initState();
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
if (state.createRoutineView) {
return const CreateNewRoutineView();
}
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Create New Routines",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 10,
),
RoutineViewCard(
onTap: () {
context.read<RoutineBloc>().add(
(ResetRoutineState()),
);
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
},
icon: Icons.add,
textString: '',
),
const SizedBox(
height: 15,
),
const Expanded(child: FetchRoutineScenesAutomation()),
],
),
);
},
);
}
}

View File

@ -1,143 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routiens/widgets/main_routine_view/routine_view_card.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
class FetchRoutineScenesAutomation extends StatefulWidget {
const FetchRoutineScenesAutomation({super.key});
@override
State<FetchRoutineScenesAutomation> createState() => _FetchRoutineScenesState();
}
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
with HelperResponsiveLayout {
@override
void initState() {
super.initState();
context.read<RoutineBloc>()
..add(const LoadScenes(spaceId, communityId))
..add(const LoadAutomation(spaceId));
}
@override
Widget build(BuildContext context) {
return BlocBuilder<RoutineBloc, RoutineState>(
builder: (context, state) {
return state.isLoading
? const Center(
child: CircularProgressIndicator(),
)
: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Scenes (Tab to Run)",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.scenes.isEmpty)
Text(
"No scenes found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.scenes.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.scenes.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetSceneDetails(
sceneId: state.scenes[index].id,
isTabToRun: true,
isUpdate: true,
),
);
},
textString: state.scenes[index].name,
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
isFromScenes: true,
iconInBytes: state.scenes[index].iconInBytes,
),
),
),
),
const SizedBox(height: 15),
Text(
"Automations",
style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
if (state.automations.isEmpty)
Text(
"No automations found",
style: context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.grayColor,
),
),
if (state.automations.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(
maxHeight: isSmallScreenSize(context) ? 160 : 170,
),
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.automations.length,
itemBuilder: (context, index) => Padding(
padding: EdgeInsets.only(
right: isSmallScreenSize(context) ? 4.0 : 8.0,
),
child: RoutineViewCard(
onTap: () {
BlocProvider.of<RoutineBloc>(context).add(
const CreateNewRoutineViewEvent(createRoutineView: true),
);
context.read<RoutineBloc>().add(
GetAutomationDetails(
automationId: state.automations[index].id,
isAutomation: true,
isUpdate: true),
);
},
textString: state.automations[index].name,
icon: state.automations[index].icon ?? Assets.automation,
),
),
),
),
],
),
),
);
},
);
}
}

View File

@ -1,7 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_event.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/effective_period/effect_period_state.dart'; import 'package:syncrow_web/pages/routines/bloc/effective_period/effect_period_state.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> { class EffectPeriodBloc extends Bloc<EffectPeriodEvent, EffectPeriodState> {

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/utils/constants/app_enum.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart';
abstract class EffectPeriodEvent extends Equatable { abstract class EffectPeriodEvent extends Equatable {

View File

@ -2,7 +2,7 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
part 'functions_bloc_event.dart'; part 'functions_bloc_event.dart';
part 'functions_bloc_state.dart'; part 'functions_bloc_state.dart';
@ -26,8 +26,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
functionCode: event.functionData.functionCode, functionCode: event.functionData.functionCode,
operationName: event.functionData.operationName, operationName: event.functionData.operationName,
value: event.functionData.value ?? existingData.value, value: event.functionData.value ?? existingData.value,
valueDescription: event.functionData.valueDescription ?? valueDescription: event.functionData.valueDescription ?? existingData.valueDescription,
existingData.valueDescription,
condition: event.functionData.condition ?? existingData.condition, condition: event.functionData.condition ?? existingData.condition,
); );
} else { } else {
@ -59,10 +58,8 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
); );
} }
FutureOr<void> _onSelectFunction( FutureOr<void> _onSelectFunction(SelectFunction event, Emitter<FunctionBlocState> emit) {
SelectFunction event, Emitter<FunctionBlocState> emit) {
emit(state.copyWith( emit(state.copyWith(
selectedFunction: event.functionCode, selectedFunction: event.functionCode, selectedOperationName: event.operationName));
selectedOperationName: event.operationName));
} }
} }

View File

@ -3,24 +3,31 @@ import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.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/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_automation_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_automation_model.dart';
import 'package:syncrow_web/pages/routiens/models/create_scene_and_autoamtion/create_scene_model.dart'; import 'package:syncrow_web/pages/routines/models/create_scene_and_autoamtion/create_scene_model.dart';
import 'package:syncrow_web/pages/routiens/models/delay/delay_fucntions.dart'; import 'package:syncrow_web/pages/routines/models/delay/delay_fucntions.dart';
import 'package:syncrow_web/pages/routiens/models/device_functions.dart'; import 'package:syncrow_web/pages/routines/models/device_functions.dart';
import 'package:syncrow_web/pages/routiens/models/routine_details_model.dart'; import 'package:syncrow_web/pages/routines/models/routine_details_model.dart';
import 'package:syncrow_web/pages/routiens/models/routine_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/devices_mang_api.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/constants/strings_manager.dart';
import 'package:syncrow_web/utils/constants/temp_const.dart';
import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart';
import 'package:syncrow_web/utils/navigation_service.dart';
import 'package:syncrow_web/utils/snack_bar.dart'; import 'package:syncrow_web/utils/snack_bar.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
part 'routine_event.dart'; part 'routine_event.dart';
part 'routine_state.dart'; part 'routine_state.dart';
const spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6'; // String spaceId = '25c96044-fadf-44bb-93c7-3c079e527ce6';
const communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9'; // String communityId = 'aff21a57-2f91-4e5c-b99b-0182c3ab65a9';
class RoutineBloc extends Bloc<RoutineEvent, RoutineState> { class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
RoutineBloc() : super(const RoutineState()) { RoutineBloc() : super(const RoutineState()) {
@ -54,11 +61,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
TriggerSwitchTabsEvent event, TriggerSwitchTabsEvent event,
Emitter<RoutineState> emit, Emitter<RoutineState> emit,
) { ) {
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false)); emit(state.copyWith(
routineTab: event.isRoutineTab, createRoutineView: false));
add(ResetRoutineState()); add(ResetRoutineState());
if (event.isRoutineTab) { if (event.isRoutineTab) {
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes());
add(const LoadAutomation(spaceId)); add(const LoadAutomation());
} }
} }
@ -80,8 +88,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems); final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = updatedIfItems.indexWhere(
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
updatedIfItems[index] = event.item; updatedIfItems[index] = event.item;
@ -90,18 +98,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
if (event.isTabToRun) { if (event.isTabToRun) {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
} else { } else {
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true)); emit(state.copyWith(
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
} }
} }
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) { void _onAddToThenContainer(
AddToThenContainer event, Emitter<RoutineState> emit) {
final currentItems = List<Map<String, dynamic>>.from(state.thenItems); final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
// Find the index of the item in teh current itemsList // Find the index of the item in teh current itemsList
int index = int index = currentItems.indexWhere(
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']); (map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
// Replace the map if the index is valid // Replace the map if the index is valid
if (index != -1) { if (index != -1) {
currentItems[index] = event.item; currentItems[index] = event.item;
@ -112,22 +123,26 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
emit(state.copyWith(thenItems: currentItems)); emit(state.copyWith(thenItems: currentItems));
} }
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) { void _onAddFunctionsToRoutine(
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
try { try {
if (event.functions.isEmpty) return; if (event.functions.isEmpty) return;
List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions); List<DeviceFunctionData> selectedFunction =
List<DeviceFunctionData>.from(event.functions);
Map<String, List<DeviceFunctionData>> currentSelectedFunctions = Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) { if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
List<DeviceFunctionData> currentFunctions = List<DeviceFunctionData> currentFunctions =
List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []); List<DeviceFunctionData>.from(
currentSelectedFunctions[event.uniqueCustomId] ?? []);
List<String> functionCode = []; List<String> functionCode = [];
for (int i = 0; i < selectedFunction.length; i++) { for (int i = 0; i < selectedFunction.length; i++) {
for (int j = 0; j < currentFunctions.length; j++) { for (int j = 0; j < currentFunctions.length; j++) {
if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) { if (selectedFunction[i].functionCode ==
currentFunctions[j].functionCode) {
currentFunctions[j] = selectedFunction[i]; currentFunctions[j] = selectedFunction[i];
if (!functionCode.contains(currentFunctions[j].functionCode)) { if (!functionCode.contains(currentFunctions[j].functionCode)) {
functionCode.add(currentFunctions[j].functionCode); functionCode.add(currentFunctions[j].functionCode);
@ -137,13 +152,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
for (int i = 0; i < functionCode.length; i++) { for (int i = 0; i < functionCode.length; i++) {
selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]); selectedFunction
.removeWhere((code) => code.functionCode == functionCode[i]);
} }
currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions) currentSelectedFunctions[event.uniqueCustomId] =
..addAll(selectedFunction); List.from(currentFunctions)..addAll(selectedFunction);
} else { } else {
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions); currentSelectedFunctions[event.uniqueCustomId] =
List.from(event.functions);
} }
emit(state.copyWith(selectedFunctions: currentSelectedFunctions)); emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
@ -152,11 +169,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async { Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try { try {
final scenes = await SceneApi.getScenesByUnitId(event.unitId, event.communityId); final projectUuid = await ProjectManager.getProjectUUID() ?? '';
BuildContext context = NavigationService.navigatorKey.currentContext!;
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));
}
}
emit(state.copyWith( emit(state.copyWith(
scenes: scenes, scenes: scenes,
isLoading: false, isLoading: false,
@ -167,46 +197,49 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
loadScenesErrorMessage: 'Failed to load scenes', loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '', errorMessage: '',
loadAutomationErrorMessage: '', loadAutomationErrorMessage: '',
)); scenes: scenes));
} }
} }
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async { Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> automations = [];
try { try {
final automations = await SceneApi.getAutomationByUnitId(event.unitId); BuildContext context = NavigationService.navigatorKey.currentContext!;
if (automations.isNotEmpty) { 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));
}
}
emit(state.copyWith( emit(state.copyWith(
automations: automations, automations: automations,
isLoading: false, isLoading: false,
)); ));
} else {
emit(state.copyWith(
isLoading: false,
loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '',
loadScenesErrorMessage: '',
));
}
} catch (e) { } catch (e) {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
loadAutomationErrorMessage: 'Failed to load automations', loadAutomationErrorMessage: 'Failed to load automations',
errorMessage: '', errorMessage: '',
loadScenesErrorMessage: '', loadScenesErrorMessage: '',
)); automations: automations));
} }
} }
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async { FutureOr<void> _onSearchRoutines(
SearchRoutines event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null)); emit(state.copyWith(isLoading: true, errorMessage: null));
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
emit(state.copyWith(isLoading: false, errorMessage: null)); emit(state.copyWith(isLoading: false, errorMessage: null));
emit(state.copyWith(searchText: event.query)); emit(state.copyWith(searchText: event.query));
} }
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) { FutureOr<void> _onAddSelectedIcon(
AddSelectedIcon event, Emitter<RoutineState> emit) {
emit(state.copyWith(selectedIcon: event.icon)); emit(state.copyWith(selectedIcon: event.icon));
} }
@ -220,7 +253,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return actions.last['deviceId'] == 'delay'; return actions.last['deviceId'] == 'delay';
} }
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateScene(
CreateSceneEvent event, Emitter<RoutineState> emit) async {
try { try {
// Check if first action is delay // Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) { // if (_isFirstActionDelay(state.thenItems)) {
@ -233,7 +267,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) { if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith( emit(state.copyWith(
errorMessage: 'A delay condition cannot be the only or the last action', errorMessage:
'A delay condition cannot be the only or the last action',
isLoading: false, isLoading: false,
)); ));
return; return;
@ -278,8 +313,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}); });
}).toList(); }).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
final createSceneModel = CreateSceneModel( final createSceneModel = CreateSceneModel(
spaceUuid: spaceId, spaceUuid: spaceBloc.state.selectedSpaces[0],
iconId: state.selectedIcon ?? '', iconId: state.selectedIcon ?? '',
showInDevice: true, showInDevice: true,
sceneName: state.routineName ?? '', sceneName: state.routineName ?? '',
@ -290,8 +328,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createScene(createSceneModel); final result = await SceneApi.createScene(createSceneModel);
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes());
add(const LoadAutomation(spaceId)); add(const LoadAutomation());
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -306,7 +344,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async { Future<void> _onCreateAutomation(
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
try { try {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith( emit(state.copyWith(
@ -327,7 +366,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) { if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith( emit(state.copyWith(
errorMessage: 'A delay condition cannot be the only or the last action', errorMessage:
'A delay condition cannot be the only or the last action',
isLoading: false, isLoading: false,
)); ));
CustomSnackBar.redSnackBar('Cannot have delay as the last action'); CustomSnackBar.redSnackBar('Cannot have delay as the last action');
@ -402,9 +442,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
); );
}); });
}).toList(); }).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
final createAutomationModel = CreateAutomationModel( final createAutomationModel = CreateAutomationModel(
spaceUuid: spaceId, spaceUuid: spaceBloc.state.selectedSpaces[0],
automationName: state.routineName ?? '', automationName: state.routineName ?? '',
decisionExpr: state.selectedAutomationOperator, decisionExpr: state.selectedAutomationOperator,
effectiveTime: EffectiveTime( effectiveTime: EffectiveTime(
@ -419,8 +461,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final result = await SceneApi.createAutomation(createAutomationModel); final result = await SceneApi.createAutomation(createAutomationModel);
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadAutomation(spaceId)); add(const LoadAutomation());
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes());
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -437,17 +479,21 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) { FutureOr<void> _onRemoveDragCard(
RemoveDragCard event, Emitter<RoutineState> emit) {
if (event.isFromThen) { if (event.isFromThen) {
final thenItems = List<Map<String, dynamic>>.from(state.thenItems); final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
thenItems.removeAt(event.index); thenItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions)); emit(state.copyWith(
thenItems: thenItems, selectedFunctions: selectedFunctions));
} else { } else {
final ifItems = List<Map<String, dynamic>>.from(state.ifItems); final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions); final selectedFunctions =
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
ifItems.removeAt(event.index); ifItems.removeAt(event.index);
selectedFunctions.remove(event.key); selectedFunctions.remove(event.key);
@ -458,7 +504,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
isAutomation: false, isAutomation: false,
isTabToRun: false)); isTabToRun: false));
} else { } else {
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions)); emit(state.copyWith(
ifItems: ifItems, selectedFunctions: selectedFunctions));
} }
} }
} }
@ -470,18 +517,23 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
)); ));
} }
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) { FutureOr<void> _onEffectiveTimeEvent(
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
emit(state.copyWith(effectiveTime: event.effectiveTime)); emit(state.copyWith(effectiveTime: event.effectiveTime));
} }
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) { FutureOr<void> _onSetRoutineName(
SetRoutineName event, Emitter<RoutineState> emit) {
emit(state.copyWith( emit(state.copyWith(
routineName: event.name, routineName: event.name,
)); ));
} }
(List<Map<String, dynamic>>, List<Map<String, dynamic>>, Map<String, List<DeviceFunctionData>>) (
_createCardData( List<Map<String, dynamic>>,
List<Map<String, dynamic>>,
Map<String, List<DeviceFunctionData>>
) _createCardData(
List<RoutineAction> actions, List<RoutineAction> actions,
List<RoutineCondition>? conditions, List<RoutineCondition>? conditions,
Map<String, List<DeviceFunctionData>> currentFunctions, Map<String, List<DeviceFunctionData>> currentFunctions,
@ -514,7 +566,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
'deviceId': condition.entityId, 'deviceId': condition.entityId,
'title': matchingDevice.name ?? condition.entityId, 'title': matchingDevice.name ?? condition.entityId,
'productType': condition.entityType, 'productType': condition.entityType,
'imagePath': matchingDevice.getDefaultIcon(condition.entityType), 'imagePath':
matchingDevice.getDefaultIcon(condition.entityType),
}; };
final functions = matchingDevice.functions; final functions = matchingDevice.functions;
@ -550,8 +603,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
final cardData = { final cardData = {
'entityId': action.entityId, 'entityId': action.entityId,
'uniqueCustomId': const Uuid().v4(), 'uniqueCustomId': const Uuid().v4(),
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'deviceId':
'title': action.actionExecutor == 'delay' ? 'Delay' : (matchingDevice.name ?? 'Device'), action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'title': action.actionExecutor == 'delay'
? 'Delay'
: (matchingDevice.name ?? 'Device'),
'productType': action.productType, 'productType': action.productType,
'imagePath': matchingDevice.getDefaultIcon(action.productType), 'imagePath': matchingDevice.getDefaultIcon(action.productType),
}; };
@ -594,7 +650,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
return (thenItems, ifItems, currentFunctions); return (thenItems, ifItems, currentFunctions);
} }
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async { Future<void> _onGetSceneDetails(
GetSceneDetails event, Emitter<RoutineState> emit) async {
try { try {
emit(state.copyWith( emit(state.copyWith(
isLoading: true, isLoading: true,
@ -642,8 +699,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (!deviceCards.containsKey(deviceId)) { if (!deviceCards.containsKey(deviceId)) {
deviceCards[deviceId] = { deviceCards[deviceId] = {
'entityId': action.entityId, 'entityId': action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'deviceId':
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay' action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId':
action.type == 'automation' || action.actionExecutor == 'delay'
? const Uuid().v4() ? const Uuid().v4()
: action.entityId, : action.entityId,
'title': action.actionExecutor == 'delay' 'title': action.actionExecutor == 'delay'
@ -680,7 +739,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
), ),
); );
// emit(state.copyWith(automationActionExecutor: action.actionExecutor)); // emit(state.copyWith(automationActionExecutor: action.actionExecutor));
} else if (action.executorProperty != null && action.actionExecutor != 'delay') { } else if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
if (!updatedFunctions.containsKey(uniqueCustomId)) { if (!updatedFunctions.containsKey(uniqueCustomId)) {
updatedFunctions[uniqueCustomId] = []; updatedFunctions[uniqueCustomId] = [];
} }
@ -752,7 +812,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) { FutureOr<void> _onResetRoutineState(
ResetRoutineState event, Emitter<RoutineState> emit) {
emit(state.copyWith( emit(state.copyWith(
ifItems: [], ifItems: [],
thenItems: [], thenItems: [],
@ -776,17 +837,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
createRoutineView: false)); createRoutineView: false));
} }
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) { FutureOr<void> _deleteScene(
DeleteScene event, Emitter<RoutineState> emit) async {
try { try {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
if (state.isTabToRun) { if (state.isTabToRun) {
SceneApi.deleteScene(unitUuid: spaceId, sceneId: state.sceneId ?? ''); await SceneApi.deleteScene(
unitUuid: spaceBloc.state.selectedSpaces[0],
sceneId: state.sceneId ?? '');
} else { } else {
SceneApi.deleteAutomation(unitUuid: spaceId, automationId: state.automationId ?? ''); await SceneApi.deleteAutomation(
unitUuid: spaceBloc.state.selectedSpaces[0],
automationId: state.automationId ?? '');
} }
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes());
add(const LoadAutomation(spaceId)); add(const LoadAutomation());
add(ResetRoutineState()); add(ResetRoutineState());
emit(state.copyWith(isLoading: false, createRoutineView: false)); emit(state.copyWith(isLoading: false, createRoutineView: false));
} catch (e) { } catch (e) {
@ -811,10 +879,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
// } // }
// } // }
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async { FutureOr<void> _fetchDevices(
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true)); emit(state.copyWith(isLoading: true));
try { try {
final devices = await DevicesManagementApi().fetchDevices(); final projectUuid = await ProjectManager.getProjectUUID() ?? '';
List<AllDevicesModel> devices = [];
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
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));
}
}
emit(state.copyWith(isLoading: false, devices: devices)); emit(state.copyWith(isLoading: false, devices: devices));
} catch (e) { } catch (e) {
@ -822,7 +904,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async { FutureOr<void> _onUpdateScene(
UpdateScene event, Emitter<RoutineState> emit) async {
try { try {
// Check if first action is delay // Check if first action is delay
// if (_isFirstActionDelay(state.thenItems)) { // if (_isFirstActionDelay(state.thenItems)) {
@ -836,7 +919,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
if (_isLastActionDelay(state.thenItems)) { if (_isLastActionDelay(state.thenItems)) {
emit(state.copyWith( emit(state.copyWith(
errorMessage: 'A delay condition cannot be the only or the last action', errorMessage:
'A delay condition cannot be the only or the last action',
isLoading: false, isLoading: false,
)); ));
return; return;
@ -889,11 +973,12 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions, actions: actions,
); );
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? ''); final result =
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadScenes(spaceId, communityId)); add(const LoadScenes());
add(const LoadAutomation(spaceId)); add(const LoadAutomation());
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -908,7 +993,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async { FutureOr<void> _onUpdateAutomation(
UpdateAutomation event, Emitter<RoutineState> emit) async {
try { try {
if (state.routineName == null || state.routineName!.isEmpty) { if (state.routineName == null || state.routineName!.isEmpty) {
emit(state.copyWith( emit(state.copyWith(
@ -1003,8 +1089,11 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}); });
}).toList(); }).toList();
BuildContext context = NavigationService.navigatorKey.currentContext!;
var spaceBloc = context.read<SpaceTreeBloc>();
final createAutomationModel = CreateAutomationModel( final createAutomationModel = CreateAutomationModel(
spaceUuid: spaceId, spaceUuid: spaceBloc.state.selectedSpaces[0],
automationName: state.routineName ?? '', automationName: state.routineName ?? '',
decisionExpr: state.selectedAutomationOperator, decisionExpr: state.selectedAutomationOperator,
effectiveTime: EffectiveTime( effectiveTime: EffectiveTime(
@ -1016,13 +1105,13 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
actions: actions, actions: actions,
); );
final result = final result = await SceneApi.updateAutomation(
await SceneApi.updateAutomation(createAutomationModel, state.automationId ?? ''); createAutomationModel, state.automationId ?? '');
if (result['success']) { if (result['success']) {
add(ResetRoutineState()); add(ResetRoutineState());
add(const LoadAutomation(spaceId)); add(LoadAutomation());
add(const LoadScenes(spaceId, communityId)); add(LoadScenes());
} else { } else {
emit(state.copyWith( emit(state.copyWith(
isLoading: false, isLoading: false,
@ -1050,7 +1139,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
thenItems: [], thenItems: [],
)); ));
final automationDetails = await SceneApi.getAutomationDetails(event.automationId); final automationDetails =
await SceneApi.getAutomationDetails(event.automationId);
final Map<String, Map<String, dynamic>> deviceIfCards = {}; final Map<String, Map<String, dynamic>> deviceIfCards = {};
final Map<String, Map<String, dynamic>> deviceThenCards = {}; final Map<String, Map<String, dynamic>> deviceThenCards = {};
@ -1118,13 +1208,15 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
), ),
); );
final deviceId = final deviceId = action.actionExecutor == 'delay'
action.actionExecutor == 'delay' ? '${action.entityId}_delay' : action.entityId; ? '${action.entityId}_delay'
: action.entityId;
if (!deviceThenCards.containsKey(deviceId)) { if (!deviceThenCards.containsKey(deviceId)) {
deviceThenCards[deviceId] = { deviceThenCards[deviceId] = {
'entityId': action.entityId, 'entityId': action.entityId,
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId, 'deviceId':
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
'uniqueCustomId': const Uuid().v4(), 'uniqueCustomId': const Uuid().v4(),
'title': action.actionExecutor == 'delay' 'title': action.actionExecutor == 'delay'
? 'Delay' ? 'Delay'
@ -1155,7 +1247,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
updatedFunctions[uniqueCustomId] = []; updatedFunctions[uniqueCustomId] = [];
} }
if (action.executorProperty != null && action.actionExecutor != 'delay') { if (action.executorProperty != null &&
action.actionExecutor != 'delay') {
final functions = matchingDevice.functions; final functions = matchingDevice.functions;
final functionCode = action.executorProperty!.functionCode; final functionCode = action.executorProperty!.functionCode;
for (var function in functions) { for (var function in functions) {
@ -1197,10 +1290,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
} }
} }
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList(); final ifItems = deviceIfCards.values
.where((card) => card['type'] == 'condition')
.toList();
final thenItems = deviceThenCards.values final thenItems = deviceThenCards.values
.where((card) => .where((card) =>
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene') card['type'] == 'action' ||
card['type'] == 'automation' ||
card['type'] == 'scene')
.toList(); .toList();
emit(state.copyWith( emit(state.copyWith(

View File

@ -27,22 +27,24 @@ class AddToThenContainer extends RoutineEvent {
} }
class LoadScenes extends RoutineEvent { class LoadScenes extends RoutineEvent {
final String unitId; // final String spaceId;
final String communityId; // final String communityId;
// final BuildContext context;
const LoadScenes(this.unitId, this.communityId); const LoadScenes();
@override @override
List<Object> get props => [unitId, communityId]; List<Object> get props => [];
} }
class LoadAutomation extends RoutineEvent { class LoadAutomation extends RoutineEvent {
final String unitId; // final String spaceId;
// final BuildContext context;
const LoadAutomation(this.unitId); const LoadAutomation();
@override @override
List<Object> get props => [unitId]; List<Object> get props => [];
} }
class AddFunctionToRoutine extends RoutineEvent { class AddFunctionToRoutine extends RoutineEvent {

View File

@ -1,7 +1,7 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_event.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_event.dart';
import 'package:syncrow_web/pages/routiens/bloc/setting_bloc/setting_state.dart'; import 'package:syncrow_web/pages/routines/bloc/setting_bloc/setting_state.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routines/models/icon_model.dart';
import 'package:syncrow_web/services/routines_api.dart'; import 'package:syncrow_web/services/routines_api.dart';
class SettingBloc extends Bloc<SettingEvent, SettingState> { class SettingBloc extends Bloc<SettingEvent, SettingState> {

View File

@ -1,5 +1,5 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:syncrow_web/pages/routiens/models/icon_model.dart'; import 'package:syncrow_web/pages/routines/models/icon_model.dart';
abstract class SettingState extends Equatable { abstract class SettingState extends Equatable {
const SettingState(); const SettingState();

View File

@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/two_gang_switch_dialog.dart';
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
class DeviceDialogHelper {
static Future<Map<String, dynamic>?> showDeviceDialog(
BuildContext context,
Map<String, dynamic> data, {
required bool removeComparetors,
}) async {
final functions = data['functions'] as List<DeviceFunction>;
try {
final result = await _getDialogForDeviceType(
context,
data['productType'],
data,
functions,
removeComparetors: removeComparetors,
);
if (result != null) {
return result;
}
} catch (e) {
debugPrint('Error: $e');
}
return null;
}
static Future<Map<String, dynamic>?> _getDialogForDeviceType(BuildContext context,
String productType, Map<String, dynamic> data, List<DeviceFunction> functions,
{required bool removeComparetors}) async {
final routineBloc = context.read<RoutineBloc>();
final deviceSelectedFunctions =
routineBloc.state.selectedFunctions[data['uniqueCustomId']] ?? [];
switch (productType) {
case 'AC':
return ACHelper.showACFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '1G':
return OneGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '2G':
return TwoGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
case '3G':
return ThreeGangSwitchHelper.showSwitchFunctionsDialog(context, functions, data['device'],
deviceSelectedFunctions, data['uniqueCustomId'], removeComparetors);
default:
return null;
}
}
}

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