Compare commits
174 Commits
SP-1367-FE
...
SP-1415
Author | SHA1 | Date | |
---|---|---|---|
09fb604acc | |||
fbc45b465f | |||
c9c939c59f | |||
e1a2465130 | |||
86164e746a | |||
4a5176cf22 | |||
2bb7a6950a | |||
46860619a0 | |||
d1bb7b129f | |||
7adce3b94c | |||
91f93d4395 | |||
42d6b64e58 | |||
b11d4186fb | |||
24130be665 | |||
8c960bd5f1 | |||
fb8ccdf0a6 | |||
1975a1b6f4 | |||
367d6717e7 | |||
8c637e40ff | |||
05b3180510 | |||
177a4711fe | |||
d7a37c6519 | |||
b901791079 | |||
5d16555748 | |||
bd15f30b8a | |||
5f2684bdf4 | |||
b4ef22ef0a | |||
d8afb562eb | |||
67a164e6d2 | |||
cf20bdcd42 | |||
32b45ea5d7 | |||
62fb8b3097 | |||
065bd33511 | |||
b9ab782c01 | |||
4d5adf948c | |||
e45f57ca03 | |||
84264391d9 | |||
2e4f904d3a | |||
c46cfb48a8 | |||
a0dd128557 | |||
34fa426163 | |||
1407c173b0 | |||
9431eb79c1 | |||
70f1f39fce | |||
f912b41fd8 | |||
977875f1f2 | |||
8136804694 | |||
ce253b2034 | |||
024fbcdb83 | |||
a8430a7d3d | |||
7ef6020dd8 | |||
2a77483f46 | |||
a6fc99443b | |||
ae95d06482 | |||
18c886753d | |||
62bf4f2944 | |||
726c173a76 | |||
d538b3667e | |||
72ae3b1727 | |||
01d5cb48cc | |||
3216d6b879 | |||
52e1ff94de | |||
0cc867a4ea | |||
3de7606a00 | |||
f709b92e12 | |||
f1667d4458 | |||
b4f03ab6c3 | |||
4c38c50649 | |||
8b441aaf46 | |||
afdd44e098 | |||
fc1d394509 | |||
dce44e20ec | |||
91c4c772b5 | |||
e0be44a507 | |||
d4a7dd5854 | |||
50eb890d18 | |||
9eefd522b7 | |||
4989a0e95c | |||
3c6b9f9ef4 | |||
86b8771694 | |||
ea1d3d18c8 | |||
9044645f95 | |||
7699453e6d | |||
d1a21be983 | |||
db8e5a4aa6 | |||
fa5bb350c3 | |||
920827d763 | |||
d3902d622e | |||
a4432656ab | |||
90e0d2f52b | |||
08e5e17910 | |||
f57348e5cd | |||
be168aed93 | |||
a66784473f | |||
c0a963ded5 | |||
7945cefe53 | |||
7d0e50fb1d | |||
117f6190dd | |||
748c67fd8b | |||
1bfab8cc76 | |||
7dcaa20da1 | |||
616adccfdd | |||
abf6555485 | |||
be0533645e | |||
254e03e3c7 | |||
db1f29e2b2 | |||
dba89027e3 | |||
6bea4c2f4a | |||
e2ec986bb9 | |||
ceb1e1d23a | |||
ee12980b47 | |||
4849bb41ba | |||
ebcd89d2a5 | |||
a7bdbfe3ec | |||
db84a9aa5e | |||
1493e35f6a | |||
f19cc616be | |||
06383018b9 | |||
9e3a78f6b7 | |||
a27b2e758c | |||
1023170788 | |||
9a2687d4c5 | |||
140f4ff5e2 | |||
cbaeecc968 | |||
2a95720cb0 | |||
4fae2d6be0 | |||
5b84076572 | |||
9bf37243a6 | |||
acad0e8c9c | |||
79f5ef7871 | |||
6493f02bcc | |||
c7c8898763 | |||
62ee9a72d6 | |||
55695ca5db | |||
c2f5a8df10 | |||
cd9821679e | |||
bfd3d4542e | |||
35a99ccda7 | |||
978934399e | |||
bc32fe7941 | |||
cb6d50d367 | |||
97eb1c152b | |||
11feaf7c87 | |||
36e88d033b | |||
c54fd780b7 | |||
fa8f29ff71 | |||
dcbca64814 | |||
c62e8b3f15 | |||
60a1a9ad6f | |||
828db5d5e4 | |||
ebbecb9738 | |||
49439816d5 | |||
a5d26d04eb | |||
74046c5aed | |||
fadb23d631 | |||
cf103d5a7c | |||
551779c33a | |||
9d3b58deeb | |||
9ca6fb8640 | |||
796409600e | |||
75b2f96428 | |||
7c68b90aef | |||
729080ec4e | |||
627ff4e929 | |||
6d612398ed | |||
3f565788d5 | |||
120ed85d10 | |||
c1d9846899 | |||
cd9ed5861a | |||
c0662bb19e | |||
5b29228738 | |||
c23176706f | |||
4ea91b9333 | |||
6c691d4b3c |
@ -25,13 +25,13 @@ jobs:
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.22.2' # Specify the Flutter version you want to use
|
||||
flutter-version: '3.27.3' # Specify the Flutter version you want to use
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Flutter Web App
|
||||
run: flutter build web --release --dart-define=FLAVOR=production
|
||||
run: flutter build web --web-renderer canvaskit -t lib/main.dart
|
||||
|
||||
- name: Build And Deploy
|
||||
id: builddeploy
|
||||
|
@ -25,13 +25,13 @@ jobs:
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.22.2' # Specify the Flutter version you want to use
|
||||
flutter-version: '3.27.3' # Specify the Flutter version you want to use
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Build Flutter Web App
|
||||
run: flutter build web --release --dart-define=FLAVOR=development
|
||||
run: flutter build web --web-renderer canvaskit -t lib/main_dev.dart
|
||||
|
||||
- name: Build And Deploy
|
||||
id: builddeploy
|
||||
|
33
.vscode/launch.json
vendored
@ -10,11 +10,12 @@
|
||||
"type": "dart",
|
||||
|
||||
"args": [
|
||||
|
||||
"--dart-define",
|
||||
|
||||
"FLAVOR=development"
|
||||
|
||||
"-d",
|
||||
"chrome",
|
||||
"--web-port",
|
||||
"3000",
|
||||
"-t",
|
||||
"lib/main_dev.dart",
|
||||
],
|
||||
|
||||
"flutterMode": "debug"
|
||||
@ -28,11 +29,12 @@
|
||||
"type": "dart",
|
||||
|
||||
"args": [
|
||||
|
||||
"--dart-define",
|
||||
|
||||
"FLAVOR=staging"
|
||||
|
||||
"-d",
|
||||
"chrome",
|
||||
"--web-port",
|
||||
"3000",
|
||||
"-t",
|
||||
"lib/main_staging.dart",
|
||||
],
|
||||
|
||||
"flutterMode": "debug"
|
||||
@ -46,11 +48,12 @@
|
||||
"type": "dart",
|
||||
|
||||
"args": [
|
||||
|
||||
"--dart-define",
|
||||
|
||||
"FLAVOR=production"
|
||||
|
||||
"-d",
|
||||
"chrome",
|
||||
"--web-port",
|
||||
"3000",
|
||||
"-t",
|
||||
"lib/main.dart",
|
||||
],
|
||||
|
||||
"flutterMode": "debug"
|
||||
|
@ -16,7 +16,12 @@ samples, guidance on mobile development, and a full API reference.
|
||||
|
||||
## USEFUL COMMANDS
|
||||
|
||||
Run on chrome: flutter run -d chrome --dart-define=FLAVOR='ENV_NAME'
|
||||
- Building for the Web
|
||||
- CanvasKit
|
||||
- `flutter build web --web-renderer canvaskit -t lib/main_dev.dart --output=build/web_dev` - build for DEVELOPMENT.
|
||||
- `flutter build web --web-renderer canvaskit -t lib/main_staging.dart --output=build/web_stg` - build for STAGING.
|
||||
- `flutter build web --web-renderer canvaskit -t lib/main.dart --output=build/web` - build for PRODUCTION.
|
||||
|
||||
- run command: `flutter run -d chrome --target=lib/main_dev.dart`
|
||||
|
||||
Build: flutter build web --release --dart-define=FLAVOR='ENV_NAME'
|
||||
|
||||
|
28
assets/icons/boundary.svg
Normal file
@ -0,0 +1,28 @@
|
||||
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.45051 0.620117H2.66797C2.76403 0.620117 2.84185 0.697982 2.84185 0.794001V16.5102H3.62439V0.794043C3.62439 0.697982 3.54657 0.620117 3.45051 0.620117Z" fill="#9C9C9C"/>
|
||||
<path d="M19.39 0.620117H18.6074C18.7035 0.620117 18.7813 0.697982 18.7813 0.794001V16.5102H19.5638V0.794043C19.5638 0.697982 19.486 0.620117 19.39 0.620117Z" fill="#9C9C9C"/>
|
||||
<path d="M2.84259 0.794001C2.84259 0.697982 2.76477 0.620117 2.66871 0.620117H2.2774C2.18134 0.620117 2.10352 0.697982 2.10352 0.794001V16.5102H2.84259V0.794001Z" fill="#ACACAC"/>
|
||||
<path d="M18.7811 0.794001C18.7811 0.697982 18.7032 0.620117 18.6072 0.620117H18.2159C18.1198 0.620117 18.042 0.697982 18.042 0.794001V16.5102H18.7811V0.794001Z" fill="#ACACAC"/>
|
||||
<path d="M7.55203 6.30395L11.8047 2.05127H8.21845L3.96582 6.30395H7.55203Z" fill="#FDB441"/>
|
||||
<path d="M11.4857 6.30395L15.7383 2.05127H11.8035L7.55078 6.30395H11.4857Z" fill="#5A5A5A"/>
|
||||
<path d="M15.0726 6.30395L19.3253 2.05127H15.739L11.4863 6.30395H15.0726Z" fill="#FDB441"/>
|
||||
<path d="M4.28295 2.05127H0.378083C0.186003 2.05127 0.0302734 2.207 0.0302734 2.39908V5.95618C0.0302734 6.0522 0.0692057 6.13916 0.132132 6.20209L4.28295 2.05127Z" fill="#FDB441"/>
|
||||
<path d="M4.28261 2.05127L0.131836 6.20209C0.194762 6.26501 0.281725 6.30395 0.377786 6.30395H3.96485L8.21748 2.05127H4.28261Z" fill="#5A5A5A"/>
|
||||
<path d="M20.6996 4.61246V2.09067L20.2053 2.05461C20.1904 2.05262 20.1752 2.05127 20.1597 2.05127H19.3259L15.0732 6.30395H19.0081C19.3229 5.98914 20.3693 4.94271 20.6996 4.61246Z" fill="#5A5A5A"/>
|
||||
<path d="M19.0078 6.30374H20.1596C20.1942 6.30374 20.2276 6.2985 20.2592 6.28906L20.6993 6.22419V4.6123C20.3688 4.94276 19.3225 5.98907 19.0078 6.30374Z" fill="#FDB441"/>
|
||||
<path d="M21.6207 2.29561C21.6105 2.26294 21.5957 2.23235 21.5769 2.20459C21.5144 2.11208 21.4086 2.05127 21.2886 2.05127C21.2344 2.05127 20.8258 2.05127 20.1582 2.05127C20.3503 2.05127 20.506 2.20696 20.506 2.39908V3.67415V4.8045L21.6364 3.67415V2.39908C21.6364 2.36298 21.6309 2.32828 21.6207 2.29561Z" fill="#444444"/>
|
||||
<path d="M20.5059 5.9563C20.5059 6.14834 20.3502 6.30407 20.1582 6.30411H21.2885C21.3365 6.30411 21.3823 6.29437 21.4239 6.27677C21.5331 6.23056 21.6134 6.13001 21.6319 6.00911C21.6345 5.99185 21.6363 5.97428 21.6363 5.9563V3.67432L20.506 4.80466V5.9563H20.5059Z" fill="#F6AB31"/>
|
||||
<path d="M7.55203 11.782L11.8047 7.5293H8.21845L3.96582 11.782H7.55203Z" fill="#FDB441"/>
|
||||
<path d="M11.4857 11.782L15.7383 7.5293H11.8035L7.55078 11.782H11.4857Z" fill="#5A5A5A"/>
|
||||
<path d="M15.0726 11.782L19.3253 7.5293H15.739L11.4863 11.782H15.0726Z" fill="#FDB441"/>
|
||||
<path d="M4.28295 7.5293H0.378083C0.186003 7.5293 0.0302734 7.68503 0.0302734 7.87711V11.4342C0.0302734 11.5302 0.0692057 11.6172 0.132132 11.6801L4.28295 7.5293Z" fill="#FDB441"/>
|
||||
<path d="M4.28261 7.52934L0.131836 11.6801C0.194762 11.743 0.281725 11.782 0.377786 11.782H3.96485L8.21752 7.5293H4.28261V7.52934Z" fill="#5A5A5A"/>
|
||||
<path d="M20.6996 10.0905V7.56869L20.2053 7.53264C20.1904 7.53065 20.1752 7.5293 20.1597 7.5293H19.3259L15.0732 11.782H19.0081C19.3229 11.4672 20.3693 10.4207 20.6996 10.0905Z" fill="#5A5A5A"/>
|
||||
<path d="M19.0078 11.7818H20.1596C20.1942 11.7818 20.2276 11.7766 20.2592 11.7671L20.6993 11.7022V10.0903C20.3688 10.4208 19.3225 11.4671 19.0078 11.7818Z" fill="#FDB441"/>
|
||||
<path d="M21.6207 7.77364C21.6105 7.74097 21.5957 7.71037 21.5769 7.68261C21.5144 7.59011 21.4086 7.5293 21.2886 7.5293C21.2344 7.5293 20.8258 7.5293 20.1582 7.5293C20.3503 7.5293 20.506 7.68498 20.506 7.87711V9.15218V10.2825L21.6364 9.15218V7.87711C21.6364 7.84105 21.6309 7.80631 21.6207 7.77364Z" fill="#444444"/>
|
||||
<path d="M20.5059 11.4343C20.5059 11.6264 20.3502 11.7821 20.1582 11.7821H21.2885C21.3365 11.7821 21.3823 11.7724 21.4239 11.7548C21.5331 11.7086 21.6134 11.608 21.6319 11.4871C21.6345 11.4699 21.6363 11.4523 21.6363 11.4343V9.15234L20.506 10.2827V11.4343H20.5059Z" fill="#F6AB31"/>
|
||||
<path d="M5.46624 15.6406H4.42285C4.5669 15.6406 4.6837 15.7574 4.6837 15.9015V17.1188C4.6837 17.2628 4.5669 17.3796 4.42285 17.3796H5.46624C5.61029 17.3796 5.72708 17.2628 5.72708 17.1188V15.9015C5.72708 15.7574 5.61029 15.6406 5.46624 15.6406Z" fill="#ACACAC"/>
|
||||
<path d="M21.4057 17.3796C21.5497 17.3796 21.6665 17.2628 21.6665 17.1188V15.9015C21.6665 15.7574 21.5497 15.6406 21.4057 15.6406H20.3623C20.5064 15.6406 20.6232 15.7574 20.6232 15.9015V17.1188C20.6232 17.2628 20.5064 17.3796 20.3623 17.3796H21.4057Z" fill="#ACACAC"/>
|
||||
<path d="M0.260846 15.6406C0.116797 15.6406 0 15.7574 0 15.9015V17.1188C0 17.2628 0.116797 17.3796 0.260846 17.3796H4.4236C4.56765 17.3796 4.6844 17.2628 4.6844 17.1188V15.9015C4.6844 15.7574 4.5676 15.6406 4.42355 15.6406H0.260846Z" fill="#BFBFBF"/>
|
||||
<path d="M16.2003 15.6406C16.0563 15.6406 15.9395 15.7574 15.9395 15.9015V17.1188C15.9395 17.2628 16.0563 17.3796 16.2003 17.3796H20.363C20.5071 17.3796 20.6239 17.2628 20.6239 17.1188V15.9015C20.6239 15.7574 20.5071 15.6406 20.363 15.6406H16.2003Z" fill="#BFBFBF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
10
assets/icons/close_to_motion.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.97266 12.0879H17.8775" stroke="#373737" stroke-linecap="round"/>
|
||||
<path d="M3.97266 11.5879C3.69651 11.5879 3.47266 11.8117 3.47266 12.0879C3.47266 12.364 3.69651 12.5879 3.97266 12.5879V11.5879ZM11.9183 12.5879H12.4183V11.5879H11.9183V12.5879ZM3.97266 12.5879H11.9183V11.5879H3.97266V12.5879Z" fill="#75777F"/>
|
||||
<path d="M3.99488 8.50684C1.71745 8.50684 0 9.54938 0 10.9319C0 12.3144 1.71745 13.3569 3.99488 13.3569C6.27232 13.3569 7.98976 12.3144 7.98976 10.9319C7.98976 9.54938 6.27236 8.50684 3.99488 8.50684Z" fill="#FFCC03"/>
|
||||
<path d="M3.99488 8.50684C1.71745 8.50684 0 9.54938 0 10.9319C0 12.3144 1.71745 13.3569 3.99488 13.3569V8.50684Z" fill="#FFE981"/>
|
||||
<path d="M6.90323 2.27066C6.24679 1.30901 5.15979 0.734863 3.99547 0.734863C2.83114 0.734863 1.74414 1.30897 1.0877 2.27066C0.431304 3.23231 0.292645 4.45382 0.716885 5.53815L2.58009 10.3005C2.80969 10.8874 3.36522 11.2667 3.99547 11.2667C4.62572 11.2667 5.18124 10.8874 5.41084 10.3005L7.27405 5.53811C7.69829 4.45382 7.55963 3.23231 6.90323 2.27066Z" fill="#CC1F2C"/>
|
||||
<path d="M3.99449 0.734863C2.83013 0.734863 1.74316 1.30897 1.08673 2.27066C0.430327 3.23231 0.291669 4.45382 0.715909 5.53815L2.57911 10.3005C2.80871 10.8874 3.36424 11.2667 3.99449 11.2667V0.734863Z" fill="#F05449"/>
|
||||
<path d="M19.5562 5.2553C19.0806 4.55855 18.293 4.14258 17.4494 4.14258C16.6058 4.14258 15.8182 4.55855 15.3426 5.2553C14.8671 5.95205 14.7666 6.83704 15.074 7.62267L16.4239 11.0731C16.5903 11.4983 16.9928 11.7731 17.4494 11.7731C17.906 11.7731 18.3086 11.4983 18.4749 11.0731L19.8248 7.62263C20.1322 6.83708 20.0317 5.95205 19.5562 5.2553Z" fill="#CC1F2C"/>
|
||||
<path d="M17.4494 4.14258C16.6058 4.14258 15.8182 4.55855 15.3426 5.2553C14.8671 5.95205 14.7666 6.83704 15.074 7.62267L16.4239 11.0731C16.5903 11.4983 16.9928 11.7731 17.4494 11.7731V4.14258Z" fill="#F05449"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
22
assets/icons/communication_fault.svg
Normal file
@ -0,0 +1,22 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.72272 14.6122L6.18327 14.354L2.97852 19.0925H6.55507L6.89972 18.8677V14.7744L6.72272 14.6122Z" fill="#407093"/>
|
||||
<path d="M6.23049 14.3775L5.01761 13.7969H4.78901L1.35938 18.8679L1.56355 19.0926H3.04156L6.23049 14.3775Z" fill="#365E7D"/>
|
||||
<path d="M7.54213 18.8677H2.81285C2.59801 18.8677 2.42383 19.0418 2.42383 19.2567V19.7497C2.42383 19.9645 2.59797 20.1386 2.81277 20.1386H7.54213C7.75698 20.1386 7.93116 19.9645 7.93116 19.7497V19.2567C7.93116 19.0418 7.75698 18.8677 7.54213 18.8677Z" fill="#4A80AA"/>
|
||||
<path d="M2.50507 19.7497V19.2567C2.50507 19.0419 2.67925 18.8677 2.89409 18.8677H0.389022C0.174179 18.8677 0 19.0418 0 19.2567V19.7497C0 19.9645 0.174179 20.1386 0.389022 20.1386H2.89405C2.67921 20.1386 2.50507 19.9645 2.50507 19.7497Z" fill="#407093"/>
|
||||
<path d="M6.06769 11.5625C5.92211 11.8628 5.83984 12.1995 5.83984 12.5557C5.83984 13.5291 6.44937 14.3595 7.30726 14.688C7.67558 14.5726 8.00285 14.3644 8.26136 14.0903V13.593L6.06769 11.5625Z" fill="#4A80AA"/>
|
||||
<path d="M5.88003 12.5635C5.88003 12.2155 5.9605 11.8866 6.10296 11.5934L5.40175 10.9443H5.11722C4.68695 11.3502 4.41797 11.9253 4.41797 12.5635C4.41797 13.7929 5.41461 14.7896 6.64406 14.7896C6.90015 14.7896 7.14573 14.7456 7.37464 14.666C6.50464 14.3633 5.88003 13.5366 5.88003 12.5635Z" fill="#407093"/>
|
||||
<path d="M18.8025 0.40287C18.4502 0.0506057 17.8791 0.0506057 17.5268 0.40287L16.666 1.26369V1.37279L17.7404 2.49283L17.9416 2.53931L18.8024 1.67849C19.1547 1.32627 19.1547 0.755135 18.8025 0.40287Z" fill="#FFD086"/>
|
||||
<path d="M19.0404 0.82865C19.0028 0.672713 18.9242 0.524628 18.8025 0.40287C18.4502 0.0506057 17.8791 0.0506057 17.5268 0.40287L16.666 1.26369V1.37279L17.2357 1.96662C17.5753 1.61322 17.9125 1.26447 18.0859 1.09107C18.3749 0.802049 18.7971 0.799822 19.0404 0.82865Z" fill="#FFC365"/>
|
||||
<path d="M15.8571 2.69561C15.8473 2.69561 15.8373 2.6951 15.8272 2.69413L5.87968 1.71499C5.71378 1.69866 5.59257 1.55097 5.6089 1.38507C5.62522 1.21917 5.773 1.09804 5.93882 1.11425L15.8864 2.09335C16.0522 2.10967 16.1735 2.25737 16.1571 2.42327C16.1418 2.57917 16.0105 2.69561 15.8571 2.69561Z" fill="#365E7D"/>
|
||||
<path d="M8.75675 4.55789C8.62288 4.55789 8.50058 4.46812 8.46503 4.33258C8.42273 4.17133 8.51917 4.00633 8.68038 3.96402L15.7804 2.10172C15.9416 2.0595 16.1066 2.15586 16.1489 2.31711C16.1912 2.47832 16.0948 2.64332 15.9336 2.68563L8.83355 4.54793C8.80784 4.55465 8.78206 4.55789 8.75675 4.55789Z" fill="#407093"/>
|
||||
<path d="M17.7843 13.6061C17.6308 13.6061 17.4995 13.4895 17.4843 13.3336L16.5112 3.37993C16.495 3.21407 16.6163 3.06641 16.7822 3.05016C16.9481 3.0347 17.0957 3.15532 17.112 3.32122L18.085 13.2749C18.1012 13.4407 17.9799 13.5884 17.814 13.6046C17.804 13.6056 17.7941 13.6061 17.7843 13.6061Z" fill="#365E7D"/>
|
||||
<path d="M14.9212 10.781C14.8956 10.781 14.8696 10.7777 14.8437 10.7709C14.6825 10.7281 14.5866 10.5629 14.6293 10.4018L16.519 3.27433C16.5617 3.11315 16.7269 3.01733 16.8881 3.05991C17.0492 3.10265 17.1452 3.26788 17.1025 3.42901L15.2127 10.5565C15.1769 10.6917 15.0548 10.781 14.9212 10.781Z" fill="#407093"/>
|
||||
<path d="M15.8027 2.12842L16.6657 1.26541L17.9412 2.54085L17.0782 3.40386L15.8027 2.12842Z" fill="#FFC365"/>
|
||||
<path d="M16.4582 2.78031C16.6656 2.56312 16.9895 2.22437 17.3028 1.89867L16.6678 1.26367L15.8047 2.12679L16.4582 2.78031Z" fill="#FFA90F"/>
|
||||
<path d="M17.2722 3.89628C17.195 3.89628 17.1177 3.86679 17.0588 3.80788L15.399 2.14805C15.2811 2.03016 15.2811 1.8391 15.399 1.72117C15.5168 1.60336 15.7079 1.60336 15.8258 1.72117L17.4857 3.38101C17.6036 3.4989 17.6036 3.68995 17.4857 3.80788C17.4267 3.86683 17.3495 3.89628 17.2722 3.89628Z" fill="#4A80AA"/>
|
||||
<path d="M17.4668 14.1492L7.36085 3.95557C6.02101 6.98032 5.78835 10.2274 6.7914 13.0274C9.56154 15.5262 13.6996 15.9099 17.5091 14.3392L17.4668 14.1492Z" fill="#B5DCFF"/>
|
||||
<path d="M7.4139 4.01031L5.09848 1.6748L4.86645 1.6977C3.23512 5.65405 3.71145 9.96509 6.47621 12.7299C6.64367 12.8973 6.81695 13.0562 6.99535 13.207C5.82988 10.4102 6.01929 7.08393 7.4139 4.01031Z" fill="#8BCAFF"/>
|
||||
<path d="M18.3911 13.4249L8.14156 3.17529C8.0407 3.19084 7.94359 3.2367 7.86594 3.31435L7.42012 3.76021C7.32437 3.85596 7.27676 3.98099 7.27539 4.1065L17.4925 14.3235C17.6444 14.4755 17.8907 14.4755 18.0426 14.3235L18.3912 13.975C18.5431 13.8231 18.5431 13.5768 18.3911 13.4249Z" fill="#DBEDFF"/>
|
||||
<path d="M7.4351 3.76805L7.87162 3.33153C7.95201 3.25114 8.05354 3.20512 8.1583 3.19266L5.78221 0.816572C5.6303 0.664658 5.38401 0.664658 5.23206 0.816572L4.88347 1.16516C4.73155 1.31708 4.73155 1.56337 4.88347 1.71528L7.29295 4.12477C7.28979 3.99602 7.33686 3.86629 7.4351 3.76805Z" fill="#B5DCFF"/>
|
||||
<path d="M14.5 10.9887C11.9318 10.9887 9.85 13.0705 9.85 15.6387C9.85 18.2068 11.9318 20.2887 14.5 20.2887C17.0682 20.2887 19.15 18.2068 19.15 15.6387C19.15 13.0705 17.0682 10.9887 14.5 10.9887ZM10.9594 15.6387C10.9594 14.4992 11.4977 13.4855 12.3344 12.8377L15.6926 18.9731C15.3201 19.1064 14.9187 19.1792 14.5 19.1792C12.5446 19.1793 10.9594 17.5941 10.9594 15.6387ZM18.0404 15.6387C18.0404 16.7782 17.5021 17.7919 16.6655 18.4397L13.3073 12.3042C13.6798 12.1709 14.0813 12.0981 14.5 12.0981C16.4554 12.0981 18.0404 13.6833 18.0404 15.6387Z" fill="#FF5A73" stroke="#FF5A73" stroke-width="0.3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.2 KiB |
15
assets/icons/cps_custom_mode.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.37727 0.166504L0 4.54378L17.2893 21.8331L21.6666 17.4557L4.37727 0.166504ZM3.1988 4.37544C2.91985 4.09649 2.91985 3.64426 3.1988 3.3653C3.47775 3.08635 3.92999 3.08635 4.20894 3.3653C4.48789 3.64426 4.48789 4.09649 4.20894 4.37544C3.92999 4.6544 3.47775 4.6544 3.1988 4.37544ZM17.4576 18.6343C17.1787 18.3553 17.1787 17.9031 17.4576 17.6241C17.7366 17.3452 18.1888 17.3452 18.4678 17.6241C18.7467 17.9031 18.7467 18.3553 18.4678 18.6343C18.1889 18.9132 17.7366 18.9132 17.4576 18.6343Z" fill="#98D9D5"/>
|
||||
<path d="M8.72863 7.2116C8.58915 7.35108 8.58915 7.57717 8.72863 7.71669C8.86811 7.85622 9.0942 7.85617 9.23372 7.71669L10.5806 6.36984L10.0755 5.86475L8.72863 7.2116Z" fill="#6DA8D6"/>
|
||||
<path d="M5.80871 4.29119C5.66923 4.43067 5.66923 4.65676 5.80871 4.79628C5.94818 4.93581 6.17428 4.93576 6.3138 4.79628L7.66066 3.44943L7.15556 2.94434L5.80871 4.29119Z" fill="#6DA8D6"/>
|
||||
<path d="M7.26867 5.75164C7.12919 5.89111 7.12919 6.11721 7.26867 6.25673C7.40815 6.39621 7.63424 6.39621 7.77376 6.25673L9.12062 4.90988L8.61552 4.40479L7.26867 5.75164Z" fill="#6DA8D6"/>
|
||||
<path d="M14.1163 12.5993C13.9768 12.7388 13.9768 12.9649 14.1163 13.1044C14.2558 13.2439 14.4819 13.2439 14.6214 13.1044L15.9683 11.7575L15.4632 11.2524L14.1163 12.5993Z" fill="#6DA8D6"/>
|
||||
<path d="M18.3841 14.1729L17.0372 15.5197C16.8977 15.6592 16.8977 15.8853 17.0372 16.0248C17.1767 16.1643 17.4028 16.1643 17.5423 16.0248L18.8892 14.6779L18.3841 14.1729Z" fill="#6DA8D6"/>
|
||||
<path d="M15.5773 14.0597C15.4378 14.1992 15.4378 14.4253 15.5773 14.5648C15.7167 14.7043 15.9428 14.7043 16.0824 14.5648L17.4292 13.218L16.9241 12.7129L15.5773 14.0597Z" fill="#6DA8D6"/>
|
||||
<path d="M21.25 1.93008L19.9031 0.583181C19.3475 0.0276114 18.4384 0.0276114 17.8828 0.583181L16.1992 2.2668L17.5461 4.28708L19.5664 5.63393L21.25 3.95031C21.8056 3.39479 21.8056 2.48565 21.25 1.93008Z" fill="#F2484B"/>
|
||||
<path d="M14.9445 3.52148L2.35938 16.1066L3.36728 18.4659L17.3015 5.2051L14.9445 3.52148Z" fill="#6DA8D6"/>
|
||||
<path d="M16.9637 4.86816L3.36621 18.4656L5.72549 19.4736L18.3106 6.88849L16.9637 4.86816Z" fill="#185F8D"/>
|
||||
<path d="M0 21.8333L2.38509 20.8512L2.02028 19.8131L0.982092 19.4482L0 21.8333Z" fill="#274B6D"/>
|
||||
<path d="M5.72586 19.4736C5.64581 19.1756 5.48467 18.8876 5.24353 18.6465C4.81015 18.2131 4.22577 18.0372 3.70329 18.129C3.7951 17.6065 3.6192 17.0222 3.18582 16.5888C2.94468 16.3476 2.65668 16.1865 2.35868 16.1064L2.35639 16.1087L0.981445 19.4478L2.38444 20.8508L5.72353 19.4759L5.72586 19.4736Z" fill="#FFCC75"/>
|
||||
<path d="M16.1991 2.26718L14.9443 3.52197L18.3115 6.88911L19.5663 5.63431L16.1991 2.26718Z" fill="#F2EBD9"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
6
assets/icons/cps_mode1.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 20.0156C15.5228 20.0156 20 15.5385 20 10.0156C20 4.49278 15.5228 0.015625 10 0.015625C4.47715 0.015625 0 4.49278 0 10.0156C0 15.5385 4.47715 20.0156 10 20.0156Z" fill="#FFB54A"/>
|
||||
<path d="M13.3263 19.4481C16.3853 18.3692 18.762 15.8448 19.636 12.6949L11.4992 4.55811C11.4992 4.55811 8.03387 7.46049 8.17614 8.25693L9.7659 9.82752L9.23356 15.3554L13.3263 19.4481Z" fill="#F9880D"/>
|
||||
<path d="M11.4996 4.55811V15.3554H9.23398V7.34521L8.17656 8.25693L6.69727 6.54092L8.86055 4.67568L8.99688 4.55811H11.4996Z" fill="#F8FFFB"/>
|
||||
<path d="M10 4.55811H11.4996V15.3554H10V4.55811Z" fill="#D8D8D8"/>
|
||||
</svg>
|
After Width: | Height: | Size: 705 B |
7
assets/icons/cps_mode2.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 20.0464C15.5228 20.0464 20 15.5692 20 10.0464C20 4.52354 15.5228 0.0463867 10 0.0463867C4.47715 0.0463867 0 4.52354 0 10.0464C0 15.5692 4.47715 20.0464 10 20.0464Z" fill="#424242"/>
|
||||
<path d="M11.0775 19.9883C15.3757 19.5277 18.8585 16.3439 19.7673 12.1961L13.3194 5.74813C13.3194 5.74813 8.04414 5.60235 6.13867 8.73633L8.9466 11.5514L6.44617 15.3861L11.0775 19.9883Z" fill="#232323"/>
|
||||
<path d="M14.159 15.3864H6.44613V12.7829L11.3996 9.03281C12.0277 8.55727 11.8763 7.89418 11.815 7.70227C11.7559 7.51742 11.5043 6.91242 10.7484 6.89035C10.7315 6.88988 10.7019 6.88961 10.69 6.88953H9.81414C8.52395 6.88953 8.42371 8.30332 8.42371 8.73664H6.13867C6.13867 6.30367 7.65008 4.60449 9.81414 4.60449L10.7029 4.60453C10.7029 4.60453 10.7776 4.6052 10.8148 4.60625C12.2924 4.64941 13.5392 5.59156 13.9916 7.00645C14.4497 8.43957 13.9737 9.95004 12.7789 10.8546L9.81121 13.1014H14.1591V15.3864H14.159Z" fill="#F8FFFB"/>
|
||||
<path d="M12.7792 10.8544L10.1514 12.844V9.97785L11.3999 9.0327C12.0279 8.55719 11.8766 7.8941 11.8155 7.70227C11.7564 7.51711 11.5046 6.91234 10.7486 6.89031C10.7317 6.88992 10.7021 6.88953 10.6903 6.88953H10.1514V4.60449H10.7033C10.7033 4.60449 10.7778 4.60527 10.8152 4.60605C12.2926 4.64938 13.5395 5.59137 13.9918 7.00652C14.45 8.43937 13.9741 9.94988 12.7792 10.8544Z" fill="#D8D8D8"/>
|
||||
<path d="M10.1514 13.1011H14.1593V15.3861H10.1514V13.1011Z" fill="#D8D8D8"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
6
assets/icons/cps_mode3.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 20.0771C15.5228 20.0771 20 15.6 20 10.0771C20 4.5543 15.5228 0.0771484 10 0.0771484C4.47715 0.0771484 0 4.5543 0 10.0771C0 15.6 4.47715 20.0771 10 20.0771Z" fill="#07E5CA"/>
|
||||
<path d="M10.0839 13.4524L8.77727 12.2934L7.35418 14.2613L12.779 19.6856C16.1417 18.7146 18.7756 16.0263 19.6699 12.6317L12.7734 5.73535L6.46289 7.4725L9.23348 10.2371L9.09187 10.8674L11.2682 12.9721C11.2682 12.972 10.5204 13.5331 10.0839 13.4524Z" fill="#00B59B"/>
|
||||
<path d="M12.7735 5.73498C12.1468 5.03658 11.1001 4.61963 9.97359 4.61963C9.74531 4.61963 9.51633 4.64061 9.29305 4.68197L9.22426 4.69467C8.53879 4.82131 7.91781 5.13639 7.42836 5.60576C6.94879 6.066 6.6284 6.64404 6.5018 7.27736L6.46289 7.47209L8.77723 7.64721L8.8243 7.52557C8.95844 7.17908 9.2818 6.91943 9.66816 6.84799L9.73695 6.83529C10.1734 6.75467 10.6217 6.90189 10.907 7.21986C11.2853 7.64131 11.2475 8.31838 10.8245 8.69842C10.6195 8.88256 9.61078 9.09518 9.27523 9.08119L9.09191 9.07334V10.867L9.27523 10.8592C9.61301 10.845 10.6196 11.0578 10.8245 11.2419C11.2475 11.622 11.2853 12.299 10.907 12.7205C10.6217 13.0385 10.1734 13.1857 9.73691 13.1051L9.6682 13.0924C9.2818 13.0209 8.95844 12.7613 8.8243 12.4148L8.77723 12.2931L6.46289 12.4683L6.5018 12.663C6.62836 13.2963 6.94875 13.8744 7.4284 14.3346C7.91777 14.804 8.53879 15.119 9.22426 15.2457L9.29297 15.2584C9.51633 15.2997 9.74531 15.3207 9.97359 15.3207C11.1001 15.3207 12.1468 14.9038 12.7734 14.2054C13.8294 13.0288 13.7796 11.1994 12.6833 9.97018C13.7796 8.74092 13.8294 6.91158 12.7735 5.73498Z" fill="#F8FFFB"/>
|
||||
<path d="M12.6836 9.97012C13.7801 11.1994 13.8297 13.0287 12.7738 14.2057C12.152 14.8982 11.1168 15.3143 10 15.3205V13.1264C10.3469 13.1197 10.6797 12.9748 10.9074 12.7205C11.2855 12.299 11.248 11.6221 10.825 11.242C10.7117 11.1404 10.3543 11.0299 10 10.9537V8.98652C10.3543 8.91035 10.7117 8.8002 10.825 8.69863C11.248 8.31855 11.2855 7.6416 10.9074 7.22012C10.6797 6.96582 10.3469 6.8209 10 6.81426V4.62012C11.1168 4.62637 12.152 5.04199 12.7738 5.73496C13.8297 6.91152 13.7801 8.74082 12.6836 9.97012Z" fill="#D8D8D8"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
6
assets/icons/cps_mode4.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10 20.1079C15.5228 20.1079 20 15.6308 20 10.1079C20 4.58506 15.5228 0.10791 10 0.10791C4.47715 0.10791 0 4.58506 0 10.1079C0 15.6308 4.47715 20.1079 10 20.1079Z" fill="#424242"/>
|
||||
<path d="M19.8765 11.6809L12.9637 4.76807L5.99414 12.7653L12.907 19.6782C16.525 18.5806 19.2749 15.4882 19.8765 11.6809Z" fill="#232323"/>
|
||||
<path d="M12.9637 10.4997V4.76807H10.0215L5.99414 10.5458V12.7653H10.698V15.4478H12.9637V12.7653H14.4117V10.4997H12.9637ZM10.698 10.4997H8.78789L10.698 7.75986V10.4997Z" fill="#F8FFFB"/>
|
||||
<path d="M12.9637 10.4997V4.76807H10.0215L10 4.79893V8.76104L10.698 7.75986V10.4997H10V12.7653H10.698V15.4478H12.9637V12.7653H14.4117V10.4997H12.9637Z" fill="#D8D8D8"/>
|
||||
</svg>
|
After Width: | Height: | Size: 787 B |
4
assets/icons/device_tag_ic.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="8" height="9" viewBox="0 0 8 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.13918 0.5H4.10294C4.0408 0.5 3.98117 0.524721 3.93722 0.568668L0.251783 4.25398C-0.0839278 4.58982 -0.0839278 5.13617 0.251783 5.47188L3.02848 8.24852C3.1906 8.41064 3.40686 8.49994 3.63734 8.5H3.6374C3.86794 8.5 4.0842 8.41064 4.24638 8.24846L7.9317 4.56308C7.97565 4.51914 8.00037 4.4595 8.00037 4.39736L8.00043 1.36113C8.00037 0.886312 7.61399 0.5 7.13918 0.5ZM7.53159 4.30031L3.91488 7.91702C3.84127 7.9907 3.74269 8.03122 3.6374 8.03122C3.53205 8.03122 3.43353 7.9907 3.35992 7.91708L0.583222 5.14045C0.43026 4.98748 0.43026 4.73845 0.583222 4.58542L4.19999 0.968775H7.13918C7.35556 0.968775 7.53165 1.14481 7.53165 1.36119L7.53159 4.30031Z" fill="#999999"/>
|
||||
<path d="M5.93455 1.8291C5.73782 1.8291 5.55288 1.90577 5.41377 2.04487C5.27466 2.18392 5.19806 2.36886 5.19806 2.56559C5.19806 2.76232 5.27466 2.94726 5.41377 3.08637C5.55288 3.22548 5.73782 3.30208 5.93455 3.30208C6.13121 3.30208 6.31616 3.22548 6.45527 3.08637C6.59437 2.94726 6.67098 2.76232 6.67098 2.56559C6.67098 2.36886 6.59437 2.18392 6.45533 2.04487C6.31622 1.90577 6.13128 1.8291 5.93455 1.8291ZM6.12383 2.75487C6.07329 2.80547 6.00602 2.83331 5.93455 2.83331C5.86301 2.83331 5.79581 2.80547 5.74527 2.75487C5.69467 2.70433 5.66683 2.63707 5.66683 2.56559C5.66683 2.49412 5.69467 2.42685 5.74527 2.37631C5.79581 2.32571 5.86307 2.29788 5.93455 2.29788C6.00602 2.29788 6.07323 2.32571 6.12383 2.37631C6.17443 2.42685 6.20226 2.49412 6.20226 2.56559C6.20226 2.63707 6.17437 2.70433 6.12383 2.75487Z" fill="#999999"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
11
assets/icons/far_away_motion.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.7889 10.2691H11.3018C10.7286 10.2691 10.2622 9.80276 10.2622 9.22952V9.14261C10.2622 8.56937 10.7286 8.10301 11.3018 8.10301H17.4112C17.7348 8.10301 17.9971 7.84067 17.9971 7.51708C17.9971 7.19349 17.7348 6.93115 17.4112 6.93115H11.3018C10.0824 6.93115 9.09034 7.92321 9.09034 9.14261V9.22952C9.09034 10.4489 10.0824 11.441 11.3018 11.441H17.7889C18.3621 11.441 18.8285 11.9073 18.8285 12.4806C18.8285 13.0538 18.3621 13.5202 17.7889 13.5202H2.48241C2.15882 13.5202 1.89648 13.7825 1.89648 14.1061C1.89648 14.4297 2.15882 14.692 2.48241 14.692H17.7889C19.0083 14.692 20.0004 13.7 20.0004 12.4806C20.0004 11.2612 19.0083 10.2691 17.7889 10.2691Z" fill="#373737"/>
|
||||
<path d="M11.3013 11.441H13.7698V10.2691H11.3013C10.7281 10.2691 10.2617 9.80276 10.2617 9.22952V9.14261C10.2617 8.56937 10.7281 8.10301 11.3013 8.10301H13.7698V6.93115H11.3013C10.0819 6.93115 9.08984 7.92321 9.08984 9.14261V9.22952C9.08984 10.4489 10.0819 11.441 11.3013 11.441Z" fill="#75777F"/>
|
||||
<path d="M13.7693 13.52H2.48144C2.15785 13.52 1.89551 13.7824 1.89551 14.1059C1.89551 14.4295 2.15785 14.6919 2.48144 14.6919H13.7693V13.52Z" fill="#75777F"/>
|
||||
<path d="M4.02221 10.8325C1.7292 10.8325 0 11.8822 0 13.2742C0 14.6661 1.7292 15.7158 4.02221 15.7158C6.31523 15.7158 8.04443 14.6661 8.04443 13.2742C8.04443 11.8822 6.31527 10.8325 4.02221 10.8325Z" fill="#FFCC03"/>
|
||||
<path d="M4.02221 10.8325C1.7292 10.8325 0 11.8822 0 13.2742C0 14.6661 1.7292 15.7158 4.02221 15.7158V10.8325Z" fill="#FFE981"/>
|
||||
<path d="M6.95014 4.55363C6.28921 3.5854 5.19477 3.00732 4.02248 3.00732C2.8502 3.00732 1.75576 3.58536 1.09483 4.55363C0.433937 5.52186 0.29433 6.75173 0.721473 7.84347L2.59742 12.6384C2.82859 13.2293 3.38792 13.6112 4.02248 13.6112C4.65705 13.6112 5.21638 13.2293 5.44754 12.6384L7.3235 7.84343C7.75064 6.75173 7.61103 5.52186 6.95014 4.55363Z" fill="#CC1F2C"/>
|
||||
<path d="M4.02151 3.00732C2.84918 3.00732 1.75478 3.58536 1.09385 4.55363C0.432961 5.52186 0.293353 6.75173 0.720496 7.84347L2.59645 12.6384C2.82762 13.2293 3.38695 13.6112 4.02151 13.6112V3.00732Z" fill="#F05449"/>
|
||||
<path d="M19.5529 1.55833C19.074 0.856811 18.2811 0.437988 17.4317 0.437988C16.5823 0.437988 15.7894 0.856811 15.3105 1.55833C14.8317 2.25984 14.7305 3.15088 15.04 3.94189L16.3992 7.41594C16.5667 7.84406 16.972 8.12074 17.4317 8.12074C17.8915 8.12074 18.2967 7.84406 18.4642 7.41594L19.8234 3.94185C20.1328 3.15092 20.0317 2.25984 19.5529 1.55833Z" fill="#CC1F2C"/>
|
||||
<path d="M17.4317 0.437988C16.5823 0.437988 15.7894 0.856811 15.3105 1.55833C14.8317 2.25984 14.7305 3.15088 15.04 3.94189L16.3992 7.41594C16.5667 7.84406 16.972 8.12074 17.4317 8.12074V0.437988Z" fill="#F05449"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
10
assets/icons/motion_meter.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.8333 21.8332C16.8164 21.8332 21.6667 16.9829 21.6667 10.9998C21.6667 5.01675 16.8164 0.166504 10.8333 0.166504C4.85025 0.166504 0 5.01675 0 10.9998C0 16.9829 4.85025 21.8332 10.8333 21.8332Z" fill="#59ABFF"/>
|
||||
<path d="M21.6663 10.9998C21.6663 5.0267 16.8061 0.166504 10.833 0.166504V21.8332C16.8061 21.8332 21.6663 16.973 21.6663 10.9998Z" fill="#4D87FF"/>
|
||||
<path d="M10.8337 19.6663C15.6201 19.6663 19.5003 15.7861 19.5003 10.9997C19.5003 6.21321 15.6201 2.33301 10.8337 2.33301C6.04719 2.33301 2.16699 6.21321 2.16699 10.9997C2.16699 15.7861 6.04719 19.6663 10.8337 19.6663Z" fill="#F3F5F9"/>
|
||||
<path d="M19.4997 10.9997C19.4997 6.22095 15.6117 2.33301 10.833 2.33301V19.6663C15.6117 19.6663 19.4997 15.7784 19.4997 10.9997Z" fill="#E1E6F0"/>
|
||||
<path d="M14.4007 7.93541C13.6771 7.21182 12.1435 6.66618 10.8333 6.66618C10.235 6.66618 9.75 6.18113 9.75 5.58285C9.75 4.98456 10.235 4.49951 10.8333 4.49951C12.7149 4.49951 14.8118 5.2824 15.9327 6.40329C16.3558 6.82637 14.6121 8.14688 14.4007 7.93541Z" fill="#FF5959"/>
|
||||
<path d="M14.4004 7.9359C14.6119 8.14744 16.3555 6.82693 15.9324 6.40385C14.8114 5.28289 12.7145 4.5 10.833 4.5V6.66667C12.1432 6.66667 13.6768 7.21231 14.4004 7.9359Z" fill="#E63A57"/>
|
||||
<path d="M10.8338 12.0833C10.534 12.0833 10.2358 11.9596 10.0216 11.7174C9.62541 11.2691 9.66758 10.5845 10.1159 10.1882L14.4493 6.35808C14.8974 5.9618 15.5821 6.00405 15.9784 6.45233C16.3746 6.90061 16.3325 7.58528 15.8841 7.98149L11.5508 11.8117C11.3447 11.9938 11.0887 12.0833 10.8338 12.0833Z" fill="#4D4D80"/>
|
||||
<path d="M14.4489 6.35808L10.833 9.55413V12.0833H10.8334C11.0884 12.0833 11.3444 11.9938 11.5505 11.8117L15.8838 7.98149C16.3322 7.58528 16.3743 6.90061 15.9781 6.45233C15.5817 6.00405 14.897 5.9618 14.4489 6.35808Z" fill="#443D66"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
9
assets/icons/moving_speed.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="22" height="18" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.6665 12.1843H13.9647V11.5495C13.9647 8.39954 11.4019 5.8367 8.25187 5.8367C5.10188 5.8367 2.53904 8.39954 2.53904 11.5495V12.1843H0V11.5495C0 8.65592 1.12686 5.93539 3.17297 3.88928C5.21908 1.84317 7.93962 0.716309 10.8332 0.716309C13.7268 0.716309 16.4474 1.84317 18.4935 3.88928C20.5396 5.93539 21.6665 8.65592 21.6665 11.5495V12.1843Z" fill="#FFCE2E"/>
|
||||
<path d="M18.4934 3.88928C16.4473 1.84317 13.7267 0.716309 10.8331 0.716309C7.93952 0.716309 5.21898 1.84317 3.17288 3.88928C3.02245 4.03954 2.87781 4.1941 2.7373 4.35146L5.1441 6.75826C6.03921 6.17574 7.10657 5.8367 8.25178 5.8367C10.8733 5.8367 13.0879 7.61188 13.7572 10.0233L21.6559 11.0867C21.5424 8.36681 20.4301 5.82596 18.4934 3.88928Z" fill="#FF9900"/>
|
||||
<path d="M13.9645 11.5495V12.1843H21.6662V11.5495C21.6662 8.65592 20.5394 5.93539 18.4933 3.88928C16.4472 1.84317 13.7266 0.716309 10.833 0.716309V6.4541C12.6895 7.39847 13.9645 9.32788 13.9645 11.5495Z" fill="#FF4F18"/>
|
||||
<path d="M18.967 3.89014L9.22326 12.7801C8.8667 13.1054 8.85381 13.6625 9.19515 14.0036L11.5747 16.3832C11.9164 16.725 12.4746 16.7116 12.7996 16.3537L21.6674 6.58985V3.89014H18.967Z" fill="#B7E4F8"/>
|
||||
<path d="M11.5735 16.3832C11.9153 16.725 12.4736 16.7116 12.7985 16.3537L21.6663 6.58985V3.89014L10.3828 15.1925L11.5735 16.3832Z" fill="#6FC8F1"/>
|
||||
<path d="M10.8328 17.2838C10.1546 17.2838 9.51683 17.0197 9.03729 16.5401C8.04746 15.5501 8.04746 13.9393 9.03729 12.9493C9.51683 12.4697 10.1546 12.2056 10.8328 12.2056C11.5109 12.2056 12.1486 12.4697 12.6281 12.9493C13.6181 13.9393 13.6181 15.55 12.6281 16.5401C12.1486 17.0197 11.5109 17.2838 10.8328 17.2838Z" fill="#388CB3"/>
|
||||
<path d="M12.628 12.9492L9.03711 16.5401C9.51665 17.0196 10.1544 17.2838 10.8326 17.2838C11.5107 17.2838 12.1484 17.0196 12.628 16.5401C13.6178 15.5501 13.6178 13.9392 12.628 12.9492Z" fill="#265D77"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
15
assets/icons/presence_judgement_threshold.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 6.47823V7.39644C0 7.51684 0.0975848 7.61442 0.217979 7.61442H8.12501V6.26025H0.217979C0.0975848 6.26025 0 6.35784 0 6.47823Z" fill="#80D261"/>
|
||||
<path d="M14.2606 0.16662C10.1904 0.144022 6.83113 3.43254 6.77133 7.50241C6.75191 8.82462 7.07708 10.0693 7.66284 11.1524C7.89233 11.5767 7.81671 12.101 7.47563 12.4422L6.82859 13.0892L7.44761 14.3853L8.74368 15.0042L9.39072 14.3572C9.73184 14.0161 10.2562 13.9405 10.6805 14.17C11.7636 14.7558 13.0083 15.0809 14.3305 15.0615C18.4003 15.0017 21.6889 11.6424 21.6663 7.57219C21.6436 3.49229 18.3405 0.18926 14.2606 0.16662Z" fill="#D4F2F6"/>
|
||||
<path d="M8.80176 7.61426C8.80176 10.6058 11.2269 13.0309 14.2184 13.0309C16.9807 13.0309 19.26 10.9633 19.5932 8.29135L17.6039 7.61426L16.2497 5.58301H14.8955L13.5413 9.64551H12.1872L10.833 6.93718L8.80176 7.61426Z" fill="#6BD9E7"/>
|
||||
<path d="M17.6041 7.61443L16.2499 5.58318H14.8957L13.5416 9.64568H12.1874L11.5103 6.93734L8.97266 6.26026C9.57399 3.92402 11.6947 2.19775 14.2187 2.19775C16.9809 2.19775 19.2602 4.26536 19.5934 6.93734L17.6041 7.61443Z" fill="#6BD9E7"/>
|
||||
<path d="M18.0722 7.77344C17.5411 10.2015 15.3802 12.0283 12.7805 12.0283C11.9447 12.0283 11.1532 11.8389 10.4463 11.5009C11.4217 12.4479 12.7523 13.031 14.2192 13.031C16.9814 13.031 19.2607 10.9634 19.5939 8.29136L18.0722 7.77344Z" fill="#02C7DD"/>
|
||||
<path d="M18.1965 6.61169C18.1965 6.89107 18.1753 7.16563 18.1344 7.4338L19.5932 6.93728C19.3609 5.07382 18.1819 3.50429 16.5527 2.7251C17.5678 3.7114 18.1965 5.09096 18.1965 6.61169Z" fill="#02C7DD"/>
|
||||
<path d="M8.31407 16.2938L8.74402 15.004L6.82893 13.0889L5.53908 13.5188C5.44276 13.5509 5.35525 13.605 5.28348 13.6768L0.319224 18.641C-0.106408 19.0666 -0.106408 19.7567 0.319224 20.1823L1.65054 21.5136C2.07617 21.9392 2.76625 21.9392 3.19184 21.5136L8.15609 16.5494C8.22791 16.4776 8.28199 16.3901 8.31407 16.2938Z" fill="#017297"/>
|
||||
<path d="M10.833 7.83257V9.23307C10.833 9.83496 11.321 10.3229 11.9229 10.3229H13.8057C14.4076 10.3229 14.8955 9.83496 14.8955 9.23303V6.47836C14.8955 6.35796 14.9931 6.26038 15.1135 6.26038H16.0317C16.1521 6.26038 16.2497 6.35796 16.2497 6.47836V7.20178C16.2497 7.80371 16.7377 8.29167 17.3396 8.29167H19.5932C19.6209 8.06984 19.6351 7.84391 19.6351 7.61459C19.6351 7.38527 19.6209 7.15933 19.5932 6.9375H17.8219C17.7015 6.9375 17.6039 6.83992 17.6039 6.71952V5.9961C17.6039 5.39422 17.1159 4.90625 16.514 4.90625H14.6312C14.0293 4.90625 13.5413 5.39422 13.5413 5.99614V8.75082C13.5413 8.87121 13.4438 8.9688 13.3234 8.9688H12.4052C12.2848 8.9688 12.1872 8.87121 12.1872 8.75082V7.35031C12.1872 6.74839 11.6992 6.26042 11.0973 6.26042H8.97238C8.861 6.69324 8.80176 7.14698 8.80176 7.61459H10.615C10.7354 7.61459 10.833 7.71217 10.833 7.83257Z" fill="#80D261"/>
|
||||
<path d="M19.5931 6.9375H18.1867C18.1588 7.40723 18.071 7.86117 17.9307 8.29167H19.5931C19.6208 8.06984 19.6351 7.8439 19.6351 7.61458C19.635 7.38526 19.6208 7.15933 19.5931 6.9375Z" fill="#68CA44"/>
|
||||
<path d="M16.1203 11.3269L12.9528 16.8131C12.7011 17.2491 13.0158 17.794 13.5191 17.794H19.854C20.3574 17.794 20.672 17.2491 20.4203 16.8131L17.2529 11.3269C17.0012 10.891 16.372 10.891 16.1203 11.3269Z" fill="#FFC344"/>
|
||||
<path d="M7.30793 15.8097C7.22427 15.8097 7.14056 15.7777 7.07675 15.7139L6.11922 14.7564C5.99151 14.6287 5.99151 14.4217 6.11922 14.294C6.2469 14.1663 6.45392 14.1663 6.58163 14.294L7.53915 15.2515C7.66687 15.3792 7.66687 15.5862 7.53915 15.7139C7.4753 15.7778 7.39159 15.8097 7.30793 15.8097Z" fill="#025F80"/>
|
||||
<path d="M16.6873 15.4124C16.5067 15.4124 16.3604 15.266 16.3604 15.0854V13.7312C16.3604 13.5507 16.5067 13.4043 16.6873 13.4043C16.8679 13.4043 17.0142 13.5507 17.0142 13.7312V15.0854C17.0142 15.2659 16.8679 15.4124 16.6873 15.4124Z" fill="#017297"/>
|
||||
<path d="M16.6863 16.7725C16.8669 16.7725 17.0133 16.6262 17.0133 16.4456C17.0133 16.265 16.8669 16.1187 16.6863 16.1187C16.5058 16.1187 16.3594 16.265 16.3594 16.4456C16.3594 16.6262 16.5058 16.7725 16.6863 16.7725Z" fill="#017297"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
14
assets/icons/radar_fault.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<svg width="21" height="21" viewBox="0 0 21 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.9996 0.169434C15.5133 0.169434 19.9996 4.6557 19.9996 10.1694C19.9996 15.6832 15.5133 20.1694 9.9996 20.1694C4.48548 20.1694 0 15.6832 0 10.1694C0 4.6557 4.48548 0.169434 9.9996 0.169434Z" fill="#DCF5F8"/>
|
||||
<path d="M10.0002 17.1381C13.8491 17.1381 16.9692 14.018 16.9692 10.1692C16.9692 6.32031 13.8491 3.2002 10.0002 3.2002C6.15136 3.2002 3.03125 6.32031 3.03125 10.1692C3.03125 14.018 6.15136 17.1381 10.0002 17.1381Z" fill="#A6E7F0"/>
|
||||
<path d="M9.99894 12.9202C11.5182 12.9202 12.7498 11.6886 12.7498 10.1694C12.7498 8.65008 11.5182 7.41846 9.99894 7.41846C8.47967 7.41846 7.24805 8.65008 7.24805 10.1694C7.24805 11.6886 8.47967 12.9202 9.99894 12.9202Z" fill="#2ED1E2"/>
|
||||
<path d="M9.99855 11.7261C10.8585 11.7261 11.5557 11.0289 11.5557 10.169C11.5557 9.30897 10.8585 8.61182 9.99855 8.61182C9.13856 8.61182 8.44141 9.30897 8.44141 10.169C8.44141 11.0289 9.13856 11.7261 9.99855 11.7261Z" fill="#02C7DD"/>
|
||||
<path d="M6.36004 0.85498L7.84432 3.53186C5.72693 4.22108 4.05125 5.89676 3.36203 8.01415L0.685547 6.53027C1.70107 3.93894 3.76871 1.8709 6.36004 0.85498Z" fill="#A6E7F0"/>
|
||||
<path d="M2.57406 9.29723C2.57406 5.89778 4.09359 2.85365 6.49005 0.805664C2.70227 2.23014 0 5.88999 0 10.1694C0 15.6831 4.48548 20.1694 9.9996 20.1694C10.3326 20.1694 10.6618 20.1527 10.9865 20.1207C6.15137 18.8944 2.57406 14.5137 2.57406 9.29723Z" fill="#A6E7F0"/>
|
||||
<path d="M7.84362 3.53174L9.99881 7.41833C8.4819 7.41833 7.24792 8.65231 7.24792 10.1692L3.36133 8.01403C4.05054 5.89664 5.72623 4.22095 7.84362 3.53174Z" fill="#6BD9E7"/>
|
||||
<path d="M6.38611 0.898474L6.36199 0.85498C3.77067 1.8709 1.70302 3.93894 0.6875 6.53027L2.6982 7.64503C3.09542 4.96823 4.44269 2.60194 6.38611 0.898474Z" fill="#6BD9E7"/>
|
||||
<path d="M8.5957 3.6387C8.5965 3.63851 8.5973 3.63837 8.59809 3.63818C8.5973 3.63837 8.5965 3.63856 8.5957 3.6387Z" fill="#A6E7F0"/>
|
||||
<path d="M14.2321 5.60636C14.7735 5.60636 15.2123 5.16752 15.2123 4.62618C15.2123 4.08484 14.7735 3.646 14.2321 3.646C13.6908 3.646 13.252 4.08484 13.252 4.62618C13.252 5.16752 13.6908 5.60636 14.2321 5.60636Z" fill="#EE6161"/>
|
||||
<path d="M7.79097 17.7514C8.22781 17.7514 8.58193 17.3972 8.58193 16.9604C8.58193 16.5236 8.22781 16.1694 7.79097 16.1694C7.35413 16.1694 7 16.5236 7 16.9604C7 17.3972 7.35413 17.7514 7.79097 17.7514Z" fill="#97DA7B"/>
|
||||
<path d="M15.5 11.0194C12.9318 11.0194 10.85 13.1013 10.85 15.6694C10.85 18.2376 12.9318 20.3194 15.5 20.3194C18.0682 20.3194 20.15 18.2376 20.15 15.6694C20.15 13.1013 18.0682 11.0194 15.5 11.0194ZM11.9594 15.6694C11.9594 14.5299 12.4977 13.5163 13.3344 12.8685L16.6926 19.0039C16.3201 19.1372 15.9187 19.2099 15.5 19.2099C13.5446 19.21 11.9594 17.6248 11.9594 15.6694ZM19.0404 15.6694C19.0404 16.8089 18.5021 17.8226 17.6655 18.4704L14.3073 12.335C14.6798 12.2017 15.0813 12.1289 15.5 12.1289C17.4554 12.1289 19.0404 13.714 19.0404 15.6694Z" fill="#FF5A73" stroke="#FF5A73" stroke-width="0.3"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
23
assets/icons/self_testing_failure.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9922 20.0771H1.75781C0.788555 20.0771 0 19.2886 0 18.3193V4.17871C0 3.20945 0.788555 2.4209 1.75781 2.4209H11.9922C12.9614 2.4209 13.75 3.20945 13.75 4.17871V18.3193C13.75 19.2886 12.9614 20.0771 11.9922 20.0771Z" fill="#FF5E82"/>
|
||||
<path d="M11.9922 2.4209H6.875V20.0771H11.9922C12.9614 20.0771 13.75 19.2886 13.75 18.3193V4.17871C13.75 3.20945 12.9614 2.4209 11.9922 2.4209Z" fill="#B7457D"/>
|
||||
<path d="M1.75781 3.59277C1.43473 3.59277 1.17188 3.85563 1.17188 4.17871V18.3193C1.17188 18.6424 1.43473 18.9053 1.75781 18.9053H11.9922C12.3153 18.9053 12.5781 18.6424 12.5781 18.3193V4.17871C12.5781 3.85563 12.3153 3.59277 11.9922 3.59277H1.75781Z" fill="#F2FAFF"/>
|
||||
<path d="M11.9922 3.59277H6.875V18.9053H11.9922C12.3153 18.9053 12.5781 18.6424 12.5781 18.3193V4.17871C12.5781 3.85563 12.3153 3.59277 11.9922 3.59277Z" fill="#C8EAFA"/>
|
||||
<path d="M5.00834 7.97561L5.76592 7.21803C5.99475 6.9892 5.99475 6.61822 5.76592 6.38939C5.53713 6.16057 5.16611 6.16057 4.93728 6.38939L4.17971 7.14697L3.42213 6.38939C3.19334 6.16057 2.82232 6.16057 2.5935 6.38939C2.36467 6.61822 2.36467 6.9892 2.5935 7.21803L3.35107 7.97561L2.5935 8.73318C2.36467 8.96201 2.36467 9.33299 2.5935 9.56182C2.70791 9.67623 2.85787 9.73342 3.00783 9.73342C3.15779 9.73342 3.30775 9.67623 3.42213 9.56178L4.17971 8.80424L4.93728 9.56182C5.05166 9.67623 5.20162 9.73342 5.35158 9.73342C5.50154 9.73342 5.6515 9.67623 5.76588 9.56178C5.99471 9.33295 5.99471 8.96197 5.76588 8.73314L5.00834 7.97561Z" fill="#5E54AC"/>
|
||||
<path d="M5.00834 11.9756L5.76592 11.218C5.99475 10.9892 5.99475 10.6182 5.76592 10.3894C5.53713 10.1606 5.16611 10.1606 4.93728 10.3894L4.17971 11.147L3.42213 10.3894C3.19334 10.1606 2.82232 10.1606 2.5935 10.3894C2.36467 10.6182 2.36467 10.9892 2.5935 11.218L3.35107 11.9756L2.5935 12.7332C2.36467 12.962 2.36467 13.333 2.5935 13.5618C2.70791 13.6762 2.85787 13.7334 3.00783 13.7334C3.15779 13.7334 3.30775 13.6762 3.42213 13.5618L4.17971 12.8042L4.93728 13.5618C5.05166 13.6762 5.20162 13.7334 5.35158 13.7334C5.50154 13.7334 5.6515 13.6762 5.76588 13.5618C5.99471 13.3329 5.99471 12.962 5.76588 12.7331L5.00834 11.9756Z" fill="#5E54AC"/>
|
||||
<path d="M5.00834 15.9756L5.76592 15.218C5.99475 14.9892 5.99475 14.6182 5.76592 14.3894C5.53713 14.1606 5.16611 14.1606 4.93728 14.3894L4.17971 15.147L3.42213 14.3894C3.19334 14.1606 2.82232 14.1606 2.5935 14.3894C2.36467 14.6182 2.36467 14.9892 2.5935 15.218L3.35107 15.9756L2.5935 16.7332C2.36467 16.962 2.36467 17.333 2.5935 17.5618C2.70791 17.6762 2.85787 17.7334 3.00783 17.7334C3.15779 17.7334 3.30775 17.6762 3.42213 17.5618L4.17971 16.8042L4.93728 17.5618C5.05166 17.6762 5.20162 17.7334 5.35158 17.7334C5.50154 17.7334 5.6515 17.6762 5.76588 17.5618C5.99471 17.3329 5.99471 16.962 5.76588 16.7331L5.00834 15.9756Z" fill="#5E54AC"/>
|
||||
<path d="M19.4141 20.0771H15.5078C15.1842 20.0771 14.9219 19.8148 14.9219 19.4912V15.585C14.9219 15.2614 15.1842 14.999 15.5078 14.999H19.4141C19.7377 14.999 20 15.2614 20 15.585V19.4912C20 19.8148 19.7377 20.0771 19.4141 20.0771Z" fill="#F2FAFF"/>
|
||||
<path d="M19.4141 14.999H17.4609V20.0771H19.4141C19.7377 20.0771 20 19.8148 20 19.4912V15.585C20 15.2614 19.7377 14.999 19.4141 14.999Z" fill="#C8EAFA"/>
|
||||
<path d="M18.2422 12.6553H16.6797C15.7104 12.6553 14.9219 13.4438 14.9219 14.4131V15.585H20V14.4131C20 13.4438 19.2114 12.6553 18.2422 12.6553Z" fill="#A4E9FF"/>
|
||||
<path d="M20 14.4131C20 13.4438 19.2114 12.6553 18.2422 12.6553H17.4609V15.585H20V14.4131Z" fill="#91BBFF"/>
|
||||
<path d="M10 4.76465H3.75C3.42641 4.76465 3.16406 4.5023 3.16406 4.17871V3.39746C3.16406 2.46688 3.8909 1.70289 4.80672 1.6434C5.06109 0.740625 5.89199 0.0771484 6.875 0.0771484C7.85801 0.0771484 8.68891 0.740625 8.94328 1.6434C9.8591 1.70289 10.5859 2.46688 10.5859 3.39746V4.17871C10.5859 4.5023 10.3236 4.76465 10 4.76465Z" fill="#FFE67A"/>
|
||||
<path d="M8.94328 1.6434C8.68891 0.740625 7.85801 0.0771484 6.875 0.0771484V4.76465H10C10.3236 4.76465 10.5859 4.5023 10.5859 4.17871V3.39746C10.5859 2.46688 9.8591 1.70289 8.94328 1.6434Z" fill="#FFC336"/>
|
||||
<path d="M7.72045 15.5587C7.52627 15.3645 7.49299 15.0614 7.64045 14.8297L9.57393 11.7914C9.69295 11.6043 9.90991 11.4972 10.1383 11.5241C10.2743 11.5401 10.399 11.6081 10.4959 11.7049L11.5741 12.7831C11.671 12.88 11.7389 13.0047 11.7549 13.1408C11.7818 13.3691 11.6748 13.5861 11.4876 13.7051L8.44928 15.6386C8.21764 15.7861 7.91463 15.7528 7.72045 15.5587Z" fill="#FFD6AA"/>
|
||||
<path d="M11.5724 12.7832L11.0333 12.2441L7.71875 15.5587C7.91293 15.7529 8.21598 15.7862 8.44766 15.6387L11.486 13.7052C11.6731 13.5862 11.7802 13.3693 11.7533 13.1409C11.7373 13.0048 11.6693 12.8801 11.5724 12.7832Z" fill="#FAC68F"/>
|
||||
<path d="M18.4926 5.89121L17.3878 4.78637C17.1589 4.55754 16.7879 4.55754 16.5591 4.78637L15.3162 6.02934C15.0873 6.25816 15.0873 6.62914 15.3162 6.85797L16.421 7.96281C16.6498 8.19164 17.0208 8.19164 17.2496 7.96281L18.4926 6.71984C18.7214 6.49105 18.7214 6.12004 18.4926 5.89121Z" fill="#F2FAFF"/>
|
||||
<path d="M17.9407 5.33887L15.8691 7.41047L16.4216 7.96289C16.6504 8.19172 17.0214 8.19172 17.2502 7.96289L18.4932 6.71992C18.722 6.49109 18.722 6.12012 18.4932 5.89129L17.9407 5.33887Z" fill="#C8EAFA"/>
|
||||
<path d="M19.5969 3.68168C19.063 3.14777 18.1973 3.14777 17.6634 3.68168L16.5586 4.78652L18.4921 6.72L19.5969 5.61516C20.1308 5.08125 20.1308 4.21559 19.5969 3.68168Z" fill="#FF5E82"/>
|
||||
<path d="M19.5989 3.68164L17.5273 5.75324L18.4941 6.72L19.5989 5.61516C20.1329 5.08121 20.1329 4.21555 19.5989 3.68164Z" fill="#B7457D"/>
|
||||
<path d="M9.65234 11.6919L15.3139 6.03037L17.2471 7.96357L11.5855 13.6251L9.65234 11.6919Z" fill="#BC8173"/>
|
||||
<path d="M10.6191 12.6587L16.2807 6.99717L17.2473 7.96377L11.5857 13.6253L10.6191 12.6587Z" fill="#9D5E4A"/>
|
||||
</svg>
|
After Width: | Height: | Size: 5.6 KiB |
23
assets/icons/self_testing_success.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9922 20.0464H1.75781C0.788555 20.0464 0 19.2578 0 18.2886V4.14795C0 3.17869 0.788555 2.39014 1.75781 2.39014H11.9922C12.9614 2.39014 13.75 3.17869 13.75 4.14795V18.2886C13.75 19.2578 12.9614 20.0464 11.9922 20.0464Z" fill="#FF5E82"/>
|
||||
<path d="M11.9922 2.39014H6.875V20.0464H11.9922C12.9614 20.0464 13.75 19.2578 13.75 18.2886V4.14795C13.75 3.17869 12.9614 2.39014 11.9922 2.39014Z" fill="#B7457D"/>
|
||||
<path d="M1.75781 3.56201C1.43473 3.56201 1.17188 3.82486 1.17188 4.14795V18.2886C1.17188 18.6117 1.43473 18.8745 1.75781 18.8745H11.9922C12.3153 18.8745 12.5781 18.6117 12.5781 18.2886V4.14795C12.5781 3.82486 12.3153 3.56201 11.9922 3.56201H1.75781Z" fill="#F2FAFF"/>
|
||||
<path d="M11.9922 3.56201H6.875V18.8745H11.9922C12.3153 18.8745 12.5781 18.6117 12.5781 18.2886V4.14795C12.5781 3.82486 12.3153 3.56201 11.9922 3.56201Z" fill="#C8EAFA"/>
|
||||
<path d="M4.01889 9.36547C3.86893 9.36547 3.71896 9.30828 3.60459 9.19383L2.49975 8.08898C2.27092 7.86016 2.27092 7.48918 2.49975 7.26035C2.72854 7.03152 3.09955 7.03152 3.32838 7.26035L4.01893 7.9509L5.81436 6.15551C6.04318 5.92668 6.4142 5.92668 6.64299 6.15551C6.87182 6.38434 6.87182 6.75531 6.64299 6.98414L4.43326 9.19383C4.31881 9.30828 4.16881 9.36547 4.01889 9.36547Z" fill="#5E54AC"/>
|
||||
<path d="M4.01889 13.2107C3.86893 13.2107 3.71896 13.1535 3.60459 13.039L2.49975 11.9342C2.27092 11.7054 2.27092 11.3344 2.49975 11.1056C2.72854 10.8767 3.09955 10.8767 3.32838 11.1056L4.01893 11.7961L5.81436 10.0007C6.04318 9.77189 6.4142 9.77189 6.64299 10.0007C6.87182 10.2296 6.87182 10.6005 6.64299 10.8294L4.43326 13.039C4.31881 13.1535 4.16881 13.2107 4.01889 13.2107Z" fill="#5E54AC"/>
|
||||
<path d="M4.01889 17.2107C3.86893 17.2107 3.71896 17.1535 3.60459 17.039L2.49975 15.9342C2.27092 15.7054 2.27092 15.3344 2.49975 15.1056C2.72854 14.8767 3.09955 14.8767 3.32838 15.1056L4.01893 15.7961L5.81436 14.0007C6.04318 13.7719 6.4142 13.7719 6.64299 14.0007C6.87182 14.2296 6.87182 14.6005 6.64299 14.8294L4.43326 17.039C4.31881 17.1535 4.16881 17.2107 4.01889 17.2107Z" fill="#5E54AC"/>
|
||||
<path d="M19.4141 20.0464H15.5078C15.1842 20.0464 14.9219 19.784 14.9219 19.4604V15.5542C14.9219 15.2306 15.1842 14.9683 15.5078 14.9683H19.4141C19.7377 14.9683 20 15.2306 20 15.5542V19.4604C20 19.784 19.7377 20.0464 19.4141 20.0464Z" fill="#F2FAFF"/>
|
||||
<path d="M19.4141 14.9683H17.4609V20.0464H19.4141C19.7377 20.0464 20 19.784 20 19.4604V15.5542C20 15.2306 19.7377 14.9683 19.4141 14.9683Z" fill="#C8EAFA"/>
|
||||
<path d="M18.2422 12.6245H16.6797C15.7104 12.6245 14.9219 13.4131 14.9219 14.3823V15.5542H20V14.3823C20 13.4131 19.2114 12.6245 18.2422 12.6245Z" fill="#A4E9FF"/>
|
||||
<path d="M20 14.3823C20 13.4131 19.2114 12.6245 18.2422 12.6245H17.4609V15.5542H20V14.3823Z" fill="#91BBFF"/>
|
||||
<path d="M10 4.73389H3.75C3.42641 4.73389 3.16406 4.47154 3.16406 4.14795V3.3667C3.16406 2.43611 3.8909 1.67213 4.80672 1.61264C5.06109 0.709863 5.89199 0.0463867 6.875 0.0463867C7.85801 0.0463867 8.68891 0.709863 8.94328 1.61264C9.8591 1.67213 10.5859 2.43611 10.5859 3.3667V4.14795C10.5859 4.47154 10.3236 4.73389 10 4.73389Z" fill="#FFE67A"/>
|
||||
<path d="M8.94328 1.61264C8.68891 0.709863 7.85801 0.0463867 6.875 0.0463867V4.73389H10C10.3236 4.73389 10.5859 4.47154 10.5859 4.14795V3.3667C10.5859 2.43611 9.8591 1.67213 8.94328 1.61264Z" fill="#FFC336"/>
|
||||
<path d="M7.72045 15.5279C7.52627 15.3337 7.49299 15.0307 7.64045 14.799L9.57393 11.7606C9.69295 11.5736 9.90991 11.4665 10.1383 11.4934C10.2743 11.5094 10.399 11.5773 10.4959 11.6742L11.5741 12.7524C11.671 12.8493 11.7389 12.9739 11.7549 13.11C11.7818 13.3384 11.6748 13.5553 11.4876 13.6743L8.44928 15.6078C8.21764 15.7554 7.91463 15.7221 7.72045 15.5279Z" fill="#FFD6AA"/>
|
||||
<path d="M11.5724 12.7525L11.0333 12.2134L7.71875 15.5279C7.91293 15.7221 8.21598 15.7554 8.44766 15.6079L11.486 13.6745C11.6731 13.5554 11.7802 13.3385 11.7533 13.1101C11.7373 12.974 11.6693 12.8494 11.5724 12.7525Z" fill="#FAC68F"/>
|
||||
<path d="M18.4926 5.86045L17.3878 4.75561C17.1589 4.52678 16.7879 4.52678 16.5591 4.75561L15.3162 5.99857C15.0873 6.2274 15.0873 6.59838 15.3162 6.82721L16.421 7.93205C16.6498 8.16088 17.0208 8.16088 17.2496 7.93205L18.4926 6.68908C18.7214 6.46029 18.7214 6.08928 18.4926 5.86045Z" fill="#F2FAFF"/>
|
||||
<path d="M17.9407 5.30811L15.8691 7.37971L16.4216 7.93213C16.6504 8.16096 17.0214 8.16096 17.2502 7.93213L18.4932 6.68916C18.722 6.46033 18.722 6.08936 18.4932 5.86053L17.9407 5.30811Z" fill="#C8EAFA"/>
|
||||
<path d="M19.5969 3.65092C19.063 3.11701 18.1973 3.11701 17.6634 3.65092L16.5586 4.75576L18.4921 6.68924L19.5969 5.58439C20.1308 5.05049 20.1308 4.18482 19.5969 3.65092Z" fill="#FF5E82"/>
|
||||
<path d="M19.5989 3.65088L17.5273 5.72248L18.4941 6.68924L19.5989 5.58439C20.1329 5.05045 20.1329 4.18479 19.5989 3.65088Z" fill="#B7457D"/>
|
||||
<path d="M9.65234 11.6611L15.3139 5.99961L17.2471 7.93281L11.5855 13.5943L9.65234 11.6611Z" fill="#BC8173"/>
|
||||
<path d="M10.6191 12.6279L16.2807 6.96641L17.2473 7.93301L11.5857 13.5945L10.6191 12.6279Z" fill="#9D5E4A"/>
|
||||
</svg>
|
After Width: | Height: | Size: 4.9 KiB |
41
assets/icons/self_testing_timeout.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9922 20.1079H1.75781C0.788555 20.1079 0 19.3194 0 18.3501V4.20947C0 3.24021 0.788555 2.45166 1.75781 2.45166H11.9922C12.9614 2.45166 13.75 3.24021 13.75 4.20947V18.3501C13.75 19.3194 12.9614 20.1079 11.9922 20.1079Z" fill="#FF5E82"/>
|
||||
<path d="M11.9922 2.45166H6.875V20.1079H11.9922C12.9614 20.1079 13.75 19.3194 13.75 18.3501V4.20947C13.75 3.24021 12.9614 2.45166 11.9922 2.45166Z" fill="#B7457D"/>
|
||||
<path d="M1.75781 3.62354C1.43473 3.62354 1.17188 3.88639 1.17188 4.20947V18.3501C1.17188 18.6732 1.43473 18.936 1.75781 18.936H11.9922C12.3153 18.936 12.5781 18.6732 12.5781 18.3501V4.20947C12.5781 3.88639 12.3153 3.62354 11.9922 3.62354H1.75781Z" fill="#F2FAFF"/>
|
||||
<path d="M11.9922 3.62354H6.875V18.936H11.9922C12.3153 18.936 12.5781 18.6732 12.5781 18.3501V4.20947C12.5781 3.88639 12.3153 3.62354 11.9922 3.62354Z" fill="#C8EAFA"/>
|
||||
<path d="M12.4999 20.1083C14.9852 20.1083 16.9999 18.0936 16.9999 15.6083C16.9999 13.1231 14.9852 11.1084 12.4999 11.1084C10.0147 11.1084 8 13.1231 8 15.6083C8 18.0936 10.0147 20.1083 12.4999 20.1083Z" fill="#F07281"/>
|
||||
<path d="M12.69 20.1039C12.6271 20.1065 12.5636 20.1078 12.5 20.1078C10.0148 20.1078 8 18.0931 8 15.6079C8 13.1227 10.0148 11.1079 12.5 11.1079C12.5636 11.1079 12.6271 11.1092 12.69 11.1119C10.293 11.2115 8.38013 13.1863 8.38013 15.6079C8.38013 18.0294 10.293 20.0043 12.69 20.1039Z" fill="#EB5569"/>
|
||||
<path d="M12.4994 19.5174C14.6586 19.5174 16.409 17.767 16.409 15.6078C16.409 13.4486 14.6586 11.6982 12.4994 11.6982C10.3402 11.6982 8.58984 13.4486 8.58984 15.6078C8.58984 17.767 10.3402 19.5174 12.4994 19.5174Z" fill="#EAF6FF"/>
|
||||
<path d="M12.7846 19.5072C12.6905 19.514 12.5952 19.5175 12.4994 19.5175C10.3402 19.5175 8.58984 17.7671 8.58984 15.6078C8.58984 13.4486 10.3402 11.6982 12.4995 11.6982C12.5953 11.6982 12.6905 11.7017 12.7846 11.7085C10.7584 11.8545 9.16007 13.5444 9.16007 15.6078C9.16007 17.6713 10.7584 19.3612 12.7846 19.5072Z" fill="#D8ECFE"/>
|
||||
<path d="M12.2539 13.3321L12.4284 13.2198C12.471 13.1924 12.5257 13.1924 12.5682 13.2198L12.7428 13.3321C12.7744 13.3525 12.7935 13.3876 12.7935 13.4252V15.6605H12.2031V13.4252C12.2031 13.3876 12.2222 13.3525 12.2539 13.3321Z" fill="#5680A6"/>
|
||||
<path d="M12.6884 13.2972L12.5833 13.3648V15.6605H12.2031V13.4353C12.2031 13.3914 12.2254 13.3504 12.2624 13.3267L12.4284 13.2198C12.471 13.1924 12.5256 13.1924 12.5682 13.2198L12.6884 13.2972Z" fill="#497090"/>
|
||||
<path d="M14.3445 15.6776L14.2376 15.8436C14.2138 15.8806 14.1729 15.9029 14.129 15.9029H12.5V15.3125H14.129C14.1729 15.3125 14.2138 15.3348 14.2376 15.3717L14.3445 15.5378C14.3719 15.5803 14.3719 15.635 14.3445 15.6776Z" fill="#5680A6"/>
|
||||
<path d="M13.2936 15.6077C13.2936 15.712 13.2734 15.8116 13.2365 15.9029H12.5V15.3125H13.2365C13.2734 15.4037 13.2936 15.5033 13.2936 15.6077Z" fill="#497090"/>
|
||||
<path d="M12.4993 16.0211C12.7277 16.0211 12.9127 15.8361 12.9127 15.6077C12.9127 15.3794 12.7277 15.1943 12.4993 15.1943C12.271 15.1943 12.0859 15.3794 12.0859 15.6077C12.0859 15.8361 12.271 16.0211 12.4993 16.0211Z" fill="#F07281"/>
|
||||
<path d="M12.6894 15.9748C12.6326 16.0044 12.568 16.0211 12.4993 16.0211C12.2711 16.0211 12.0859 15.836 12.0859 15.6077C12.0859 15.3795 12.2711 15.1943 12.4993 15.1943C12.568 15.1943 12.6326 15.2111 12.6894 15.2407C12.5567 15.3093 12.4661 15.4481 12.4661 15.6078C12.4661 15.7674 12.5567 15.9061 12.6894 15.9748Z" fill="#EB5569"/>
|
||||
<path d="M12.5019 12.7788C12.4232 12.7788 12.3594 12.715 12.3594 12.6363V12.268C12.3594 12.1893 12.4232 12.1255 12.5019 12.1255C12.5807 12.1255 12.6445 12.1893 12.6445 12.268V12.6363C12.6445 12.715 12.5807 12.7788 12.5019 12.7788Z" fill="#88B4F5"/>
|
||||
<path d="M12.5019 19.0894C12.4232 19.0894 12.3594 19.0255 12.3594 18.9468V18.5786C12.3594 18.4999 12.4232 18.436 12.5019 18.436C12.5807 18.436 12.6445 18.4999 12.6445 18.5786V18.9468C12.6445 19.0255 12.5807 19.0894 12.5019 19.0894Z" fill="#88B4F5"/>
|
||||
<path d="M9.52445 15.7504H9.15622C9.07749 15.7504 9.01367 15.6866 9.01367 15.6079C9.01367 15.5292 9.07749 15.4653 9.15622 15.4653H9.52445C9.60318 15.4653 9.667 15.5292 9.667 15.6079C9.667 15.6866 9.60318 15.7504 9.52445 15.7504Z" fill="#88B4F5"/>
|
||||
<path d="M15.835 15.7504H15.4668C15.388 15.7504 15.3242 15.6866 15.3242 15.6079C15.3242 15.5292 15.388 15.4653 15.4668 15.4653H15.835C15.9137 15.4653 15.9775 15.5292 15.9775 15.6079C15.9775 15.6866 15.9137 15.7504 15.835 15.7504Z" fill="#88B4F5"/>
|
||||
<path d="M9.60755 17.4199C9.55829 17.4199 9.51037 17.3944 9.48397 17.3486C9.4446 17.2805 9.46796 17.1933 9.53615 17.1539L9.85505 16.9698C9.92318 16.9305 10.0104 16.9538 10.0498 17.022C10.0891 17.0901 10.0658 17.1773 9.99759 17.2167L9.6787 17.4008C9.65626 17.4138 9.63174 17.4199 9.60755 17.4199Z" fill="#88B4F5"/>
|
||||
<path d="M15.0724 14.2647C15.0231 14.2647 14.9752 14.2391 14.9488 14.1934C14.9094 14.1252 14.9328 14.038 15.001 13.9986L15.3199 13.8145C15.388 13.7752 15.4752 13.7985 15.5146 13.8667C15.554 13.9349 15.5306 14.0221 15.4624 14.0614L15.1435 14.2456C15.1211 14.2585 15.0966 14.2647 15.0724 14.2647Z" fill="#88B4F5"/>
|
||||
<path d="M9.92619 14.2647C9.902 14.2647 9.8775 14.2585 9.85505 14.2456L9.53615 14.0614C9.46796 14.0221 9.4446 13.9349 9.48397 13.8667C9.52333 13.7985 9.6105 13.7752 9.6787 13.8145L9.99759 13.9987C10.0658 14.038 10.0891 14.1252 10.0498 14.1934C10.0234 14.2391 9.97545 14.2647 9.92619 14.2647Z" fill="#88B4F5"/>
|
||||
<path d="M10.8319 18.6422C10.8077 18.6422 10.7832 18.636 10.7608 18.623C10.6926 18.5836 10.6692 18.4965 10.7086 18.4283L10.8927 18.1094C10.9321 18.0412 11.0192 18.0178 11.0874 18.0572C11.1556 18.0966 11.179 18.1838 11.1396 18.2519L10.9555 18.5708C10.9291 18.6166 10.8812 18.6422 10.8319 18.6422Z" fill="#88B4F5"/>
|
||||
<path d="M13.9862 13.1773C13.962 13.1773 13.9375 13.1711 13.9151 13.1582C13.8469 13.1188 13.8235 13.0316 13.8629 12.9634L14.047 12.6445C14.0863 12.5764 14.1735 12.553 14.2417 12.5924C14.3099 12.6317 14.3333 12.7189 14.2939 12.7871L14.1098 13.106C14.0834 13.1517 14.0355 13.1773 13.9862 13.1773Z" fill="#88B4F5"/>
|
||||
<path d="M11.0163 13.1778C10.967 13.1778 10.9191 13.1522 10.8927 13.1065L10.7086 12.7876C10.6692 12.7194 10.6926 12.6322 10.7608 12.5929C10.8289 12.5535 10.9161 12.5768 10.9555 12.645L11.1396 12.9639C11.179 13.0321 11.1556 13.1193 11.0874 13.1587C11.065 13.1716 11.0405 13.1778 11.0163 13.1778Z" fill="#88B4F5"/>
|
||||
<path d="M15.3252 15.6128C15.3252 15.534 15.389 15.4702 15.4677 15.4702L15.836 15.4702C15.9147 15.4702 15.9785 15.534 15.9785 15.6128C15.9785 15.6915 15.9147 15.7553 15.836 15.7553L15.4677 15.7553C15.389 15.7553 15.3252 15.6915 15.3252 15.6128Z" fill="#88B4F5"/>
|
||||
<path d="M9.01464 15.6128C9.01464 15.534 9.07846 15.4702 9.15719 15.4702L9.52542 15.4702C9.60415 15.4702 9.66797 15.534 9.66797 15.6128C9.66797 15.6915 9.60415 15.7553 9.52542 15.7553L9.15719 15.7553C9.07848 15.7553 9.01464 15.6915 9.01464 15.6128Z" fill="#88B4F5"/>
|
||||
<path d="M12.3536 12.6363L12.3536 12.268C12.3536 12.1893 12.4174 12.1255 12.4961 12.1255C12.5749 12.1255 12.6387 12.1893 12.6387 12.268L12.6387 12.6363C12.6387 12.715 12.5749 12.7788 12.4961 12.7788C12.4174 12.7788 12.3536 12.715 12.3536 12.6363Z" fill="#88B4F5"/>
|
||||
<path d="M12.3536 18.9468L12.3536 18.5786C12.3536 18.4998 12.4174 18.436 12.4961 18.436C12.5749 18.436 12.6387 18.4999 12.6387 18.5786L12.6387 18.9468C12.6387 19.0256 12.5749 19.0894 12.4961 19.0894C12.4174 19.0894 12.3536 19.0255 12.3536 18.9468Z" fill="#88B4F5"/>
|
||||
<path d="M10.6831 12.7184C10.6831 12.6691 10.7087 12.6212 10.7544 12.5948C10.8226 12.5554 10.9098 12.5788 10.9491 12.647L11.1332 12.9659C11.1726 13.034 11.1492 13.1212 11.0811 13.1606C11.0129 13.2 10.9257 13.1766 10.8863 13.1084L10.7022 12.7895C10.6892 12.7671 10.6831 12.7426 10.6831 12.7184Z" fill="#88B4F5"/>
|
||||
<path d="M13.8393 18.1832C13.8393 18.134 13.8649 18.0861 13.9106 18.0597C13.9788 18.0203 14.066 18.0437 14.1054 18.1118L14.2895 18.4307C14.3288 18.4989 14.3055 18.5861 14.2373 18.6255C14.1691 18.6648 14.0819 18.6414 14.0426 18.5733L13.8585 18.2544C13.8455 18.2319 13.8393 18.2074 13.8393 18.1832Z" fill="#88B4F5"/>
|
||||
<path d="M13.8393 13.037C13.8393 13.0128 13.8455 12.9883 13.8584 12.9659L14.0426 12.647C14.0819 12.5788 14.1691 12.5554 14.2373 12.5948C14.3055 12.6342 14.3288 12.7213 14.2895 12.7895L14.1053 13.1084C14.066 13.1766 13.9788 13.2 13.9106 13.1606C13.8649 13.1342 13.8393 13.0863 13.8393 13.037Z" fill="#88B4F5"/>
|
||||
<path d="M9.46038 13.9437C9.46038 13.9195 9.46655 13.895 9.47952 13.8726C9.51889 13.8044 9.60607 13.781 9.67424 13.8204L9.99314 14.0045C10.0613 14.0439 10.0847 14.1311 10.0453 14.1992C10.0059 14.2674 9.91877 14.2908 9.85059 14.2514L9.53169 14.0673C9.48596 14.0409 9.46038 13.993 9.46038 13.9437Z" fill="#88B4F5"/>
|
||||
<path d="M14.9252 17.099C14.9252 17.0748 14.9314 17.0503 14.9444 17.0278C14.9837 16.9597 15.0709 16.9363 15.1391 16.9757L15.458 17.1598C15.5262 17.1991 15.5495 17.2863 15.5102 17.3545C15.4708 17.4227 15.3836 17.4461 15.3154 17.4067L14.9965 17.2226C14.9508 17.1962 14.9252 17.1483 14.9252 17.099Z" fill="#88B4F5"/>
|
||||
<path d="M14.9252 14.1281C14.9252 14.0788 14.9508 14.0309 14.9965 14.0045L15.3154 13.8204C15.3836 13.781 15.4708 13.8044 15.5102 13.8726C15.5495 13.9407 15.5262 14.0279 15.458 14.0673L15.1391 14.2514C15.0709 14.2908 14.9837 14.2674 14.9444 14.1992C14.9314 14.1768 14.9252 14.1523 14.9252 14.1281Z" fill="#88B4F5"/>
|
||||
<path d="M4.01889 9.27221C3.86893 9.27221 3.71896 9.21502 3.60459 9.10057L2.49975 7.99572C2.27092 7.76689 2.27092 7.39592 2.49975 7.16709C2.72854 6.93826 3.09955 6.93826 3.32838 7.16709L4.01893 7.85764L5.81436 6.06225C6.04318 5.83342 6.4142 5.83342 6.64299 6.06225C6.87182 6.29107 6.87182 6.66205 6.64299 6.89088L4.43326 9.10057C4.31881 9.21498 4.16881 9.27221 4.01889 9.27221Z" fill="#5E54AC"/>
|
||||
<path d="M5.00834 12.0069L5.76592 11.2493C5.99475 11.0204 5.99475 10.6495 5.76592 10.4206C5.53713 10.1918 5.16611 10.1918 4.93728 10.4206L4.17971 11.1782L3.42213 10.4206C3.19334 10.1918 2.82232 10.1918 2.5935 10.4206C2.36467 10.6495 2.36467 11.0204 2.5935 11.2493L3.35107 12.0069L2.5935 12.7644C2.36467 12.9933 2.36467 13.3642 2.5935 13.5931C2.70791 13.7075 2.85787 13.7647 3.00783 13.7647C3.15779 13.7647 3.30775 13.7075 3.42213 13.593L4.17971 12.8355L4.93728 13.5931C5.05166 13.7075 5.20162 13.7647 5.35158 13.7647C5.50154 13.7647 5.6515 13.7075 5.76588 13.593C5.99471 13.3642 5.99471 12.9932 5.76588 12.7644L5.00834 12.0069Z" fill="#5E54AC"/>
|
||||
<path d="M5.00834 16.0069L5.76592 15.2493C5.99475 15.0204 5.99475 14.6495 5.76592 14.4206C5.53713 14.1918 5.16611 14.1918 4.93728 14.4206L4.17971 15.1782L3.42213 14.4206C3.19334 14.1918 2.82232 14.1918 2.5935 14.4206C2.36467 14.6495 2.36467 15.0204 2.5935 15.2493L3.35107 16.0069L2.5935 16.7644C2.36467 16.9933 2.36467 17.3642 2.5935 17.5931C2.70791 17.7075 2.85787 17.7647 3.00783 17.7647C3.15779 17.7647 3.30775 17.7075 3.42213 17.593L4.17971 16.8355L4.93728 17.5931C5.05166 17.7075 5.20162 17.7647 5.35158 17.7647C5.50154 17.7647 5.6515 17.7075 5.76588 17.593C5.99471 17.3642 5.99471 16.9932 5.76588 16.7644L5.00834 16.0069Z" fill="#5E54AC"/>
|
||||
<path d="M10 4.79541H3.75C3.42641 4.79541 3.16406 4.53307 3.16406 4.20947V3.42822C3.16406 2.49764 3.8909 1.73365 4.80672 1.67416C5.06109 0.771387 5.89199 0.10791 6.875 0.10791C7.85801 0.10791 8.68891 0.771387 8.94328 1.67416C9.8591 1.73365 10.5859 2.49764 10.5859 3.42822V4.20947C10.5859 4.53307 10.3236 4.79541 10 4.79541Z" fill="#FFE67A"/>
|
||||
<path d="M8.94328 1.67416C8.68891 0.771387 7.85801 0.10791 6.875 0.10791V4.79541H10C10.3236 4.79541 10.5859 4.53307 10.5859 4.20947V3.42822C10.5859 2.49764 9.8591 1.73365 8.94328 1.67416Z" fill="#FFC336"/>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
5
assets/icons/sensitivity_feature_1.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.72751C16.7022 7.47106 16.444 7.24286 16.1963 7.34188L15.166 7.75403C16.1394 3.617 13.6255 1.13192 12.3309 0.160156L11.9303 0.68629C12.295 4.20985 10.8697 6.77552 10.8697 6.77552L9.6861 6.06286C7.63047 8.86439 7.26813 11.637 7.26813 11.637L6.22457 10.9497L5.84278 15.599L4.19531 17.1113C4.19531 17.1113 9.12215 19.2668 13.862 14.8542C14.0281 14.6996 13.9924 14.4272 13.7895 14.3258L12.2619 13.562C13.7729 12.6635 15.8697 10.3879 16.629 7.72751Z" fill="#1479FF"/>
|
||||
<path d="M6.94955 12.0658C7.20748 12.3235 7.64893 12.1695 7.69061 11.8073L7.74029 11.3759C7.74315 11.3515 8.03272 8.98838 9.75631 6.48396L10.3884 7.11599C10.5932 7.32076 10.9371 7.27255 11.0777 7.01939L11.2124 6.77685C11.2795 6.65646 12.7674 3.91306 12.3306 0.160279C12.2926 0.131763 12.2554 0.104185 12.2196 0.0782866C11.9917 -0.0867917 11.68 0.0943414 11.709 0.374303C12.0737 3.89786 10.6483 6.46353 10.6483 6.46353L9.94327 5.75849C9.80448 5.6197 9.57174 5.63474 9.45561 5.79298C7.40002 8.59451 7.099 11.3035 7.099 11.3035L6.50061 10.7051C6.32693 10.5315 6.03064 10.6084 5.96029 10.8437C5.23283 13.2767 5.71564 15.7659 5.71564 15.7659L5.71514 15.7664L6.34936 15.6438C6.34525 15.6222 5.97377 13.6265 6.44041 11.5575L6.94955 12.0658Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.00624C13.3141 5.83764 13.224 5.65428 13.0554 5.59659C12.8865 5.5394 12.7031 5.62905 12.6457 5.7976C11.8359 8.16772 10.4981 10.2792 8.9927 12.0909C8.98586 10.3632 9.52113 9.3046 9.5316 9.28437C9.61414 9.12683 9.55367 8.93194 9.39641 8.8489C9.23793 8.76601 9.04348 8.82636 8.96031 8.98394C8.92824 9.04499 8.19051 10.4827 8.37992 12.7999C4.65574 16.9746 0.236005 19.3733 0.170849 19.408C0.0136223 19.492 -0.0459481 19.6873 0.0381926 19.8446C0.0961615 19.9534 0.207724 20.0154 0.323037 20.0154C0.374404 20.0154 0.426084 20.0032 0.474599 19.9774C0.530303 19.9477 3.69996 18.2289 6.9675 15.1631C7.06207 15.1709 7.47437 15.2031 7.91512 15.2031C8.76023 15.2031 10.083 15.1232 11.1436 14.6989C11.3091 14.6326 11.3897 14.4449 11.3236 14.2793C11.2574 14.1139 11.068 14.034 10.9041 14.0996C9.80613 14.5389 8.34164 14.5695 7.6 14.552C9.86817 12.3043 12.09 9.41976 13.2564 6.00624Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_2.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.75827C16.7022 7.50183 16.444 7.27362 16.1963 7.37265L15.166 7.78479C16.1394 3.64776 13.6255 1.16268 12.3309 0.190918L11.9303 0.717051C12.295 4.24061 10.8697 6.80628 10.8697 6.80628L9.6861 6.09362C7.63047 8.89515 7.26813 11.6677 7.26813 11.6677L6.22457 10.9805L5.84278 15.6298L4.19531 17.1421C4.19531 17.1421 9.12215 19.2976 13.862 14.8849C14.0281 14.7303 13.9924 14.458 13.7895 14.3565L12.2619 13.5927C13.7729 12.6943 15.8697 10.4187 16.629 7.75827Z" fill="#3C90FF"/>
|
||||
<path d="M6.94955 12.0966C7.20748 12.3542 7.64893 12.2002 7.69061 11.838L7.74029 11.4067C7.74315 11.3823 8.03272 9.01914 9.75631 6.51472L10.3884 7.14675C10.5932 7.35152 10.9371 7.30332 11.0777 7.05015L11.2124 6.80761C11.2795 6.68722 12.7674 3.94382 12.3306 0.191041C12.2926 0.162525 12.2554 0.134947 12.2196 0.109048C11.9917 -0.05603 11.68 0.125103 11.709 0.405064C12.0737 3.92862 10.6483 6.49429 10.6483 6.49429L9.94327 5.78925C9.80448 5.65046 9.57174 5.6655 9.45561 5.82374C7.40002 8.62527 7.099 11.3343 7.099 11.3343L6.50061 10.7359C6.32693 10.5622 6.03064 10.6391 5.96029 10.8745C5.23283 13.3075 5.71564 15.7967 5.71564 15.7967L5.71514 15.7971L6.34936 15.6745C6.34525 15.6529 5.97377 13.6573 6.44041 11.5883L6.94955 12.0966Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.037C13.3141 5.86841 13.224 5.68505 13.0554 5.62735C12.8865 5.57016 12.7031 5.65981 12.6457 5.82837C11.8359 8.19849 10.4981 10.3099 8.9927 12.1216C8.98586 10.394 9.52113 9.33536 9.5316 9.31513C9.61414 9.15759 9.55367 8.96271 9.39641 8.87966C9.23793 8.79677 9.04348 8.85712 8.96031 9.0147C8.92824 9.07575 8.19051 10.5135 8.37992 12.8306C4.65574 17.0054 0.236005 19.404 0.170849 19.4388C0.0136223 19.5228 -0.0459481 19.7181 0.0381926 19.8753C0.0961615 19.9842 0.207724 20.0461 0.323037 20.0461C0.374404 20.0461 0.426084 20.034 0.474599 20.0081C0.530303 19.9785 3.69996 18.2597 6.9675 15.1939C7.06207 15.2017 7.47437 15.2338 7.91512 15.2338C8.76023 15.2338 10.083 15.154 11.1436 14.7297C11.3091 14.6633 11.3897 14.4757 11.3236 14.3101C11.2574 14.1447 11.068 14.0648 10.9041 14.1303C9.80613 14.5696 8.34164 14.6003 7.6 14.5828C9.86817 12.335 12.09 9.45052 13.2564 6.037Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_3.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.78903C16.7022 7.53259 16.444 7.30438 16.1963 7.40341L15.166 7.81556C16.1394 3.67852 13.6255 1.19344 12.3309 0.22168L11.9303 0.747813C12.295 4.27137 10.8697 6.83704 10.8697 6.83704L9.6861 6.12438C7.63047 8.92591 7.26813 11.6985 7.26813 11.6985L6.22457 11.0113L5.84278 15.6606L4.19531 17.1728C4.19531 17.1728 9.12215 19.3283 13.862 14.9157C14.0281 14.7611 13.9924 14.4887 13.7895 14.3873L12.2619 13.6235C13.7729 12.725 15.8697 10.4494 16.629 7.78903Z" fill="#59A0FF"/>
|
||||
<path d="M6.94955 12.1274C7.20748 12.385 7.64893 12.231 7.69061 11.8688L7.74029 11.4374C7.74315 11.413 8.03272 9.0499 9.75631 6.54548L10.3884 7.17751C10.5932 7.38228 10.9371 7.33408 11.0777 7.08091L11.2124 6.83837C11.2795 6.71798 12.7674 3.97458 12.3306 0.221802C12.2926 0.193287 12.2554 0.165709 12.2196 0.13981C11.9917 -0.0252682 11.68 0.155865 11.709 0.435826C12.0737 3.95939 10.6483 6.52505 10.6483 6.52505L9.94327 5.82001C9.80448 5.68122 9.57174 5.69626 9.45561 5.8545C7.40002 8.65603 7.099 11.3651 7.099 11.3651L6.50061 10.7667C6.32693 10.593 6.03064 10.6699 5.96029 10.9052C5.23283 13.3383 5.71564 15.8274 5.71564 15.8274L5.71514 15.8279L6.34936 15.7053C6.34525 15.6837 5.97377 13.688 6.44041 11.619L6.94955 12.1274Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.06776C13.3141 5.89917 13.224 5.71581 13.0554 5.65811C12.8865 5.60092 12.7031 5.69057 12.6457 5.85913C11.8359 8.22925 10.4981 10.3407 8.9927 12.1524C8.98586 10.4247 9.52113 9.36612 9.5316 9.34589C9.61414 9.18835 9.55367 8.99347 9.39641 8.91042C9.23793 8.82753 9.04348 8.88788 8.96031 9.04546C8.92824 9.10651 8.19051 10.5442 8.37992 12.8614C4.65574 17.0362 0.236005 19.4348 0.170849 19.4695C0.0136223 19.5535 -0.0459481 19.7489 0.0381926 19.9061C0.0961615 20.015 0.207724 20.0769 0.323037 20.0769C0.374404 20.0769 0.426084 20.0647 0.474599 20.0389C0.530303 20.0093 3.69996 18.2904 6.9675 15.2246C7.06207 15.2324 7.47437 15.2646 7.91512 15.2646C8.76023 15.2646 10.083 15.1847 11.1436 14.7604C11.3091 14.6941 11.3897 14.5064 11.3236 14.3409C11.2574 14.1754 11.068 14.0955 10.9041 14.1611C9.80613 14.6004 8.34164 14.6311 7.6 14.6136C9.86817 12.3658 12.09 9.48128 13.2564 6.06776Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_4.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.81979C16.7022 7.56335 16.444 7.33515 16.1963 7.43417L15.166 7.84632C16.1394 3.70928 13.6255 1.2242 12.3309 0.252441L11.9303 0.778575C12.295 4.30213 10.8697 6.8678 10.8697 6.8678L9.6861 6.15514C7.63047 8.95667 7.26813 11.7293 7.26813 11.7293L6.22457 11.042L5.84278 15.6913L4.19531 17.2036C4.19531 17.2036 9.12215 19.3591 13.862 14.9464C14.0281 14.7919 13.9924 14.5195 13.7895 14.418L12.2619 13.6543C13.7729 12.7558 15.8697 10.4802 16.629 7.81979Z" fill="#6AAAFF"/>
|
||||
<path d="M6.94955 12.1581C7.20748 12.4157 7.64893 12.2618 7.69061 11.8996L7.74029 11.4682C7.74315 11.4438 8.03272 9.08066 9.75631 6.57624L10.3884 7.20828C10.5932 7.41304 10.9371 7.36484 11.0777 7.11167L11.2124 6.86913C11.2795 6.74874 12.7674 4.00534 12.3306 0.252564C12.2926 0.224048 12.2554 0.19647 12.2196 0.170572C11.9917 0.00549348 11.68 0.186627 11.709 0.466588C12.0737 3.99015 10.6483 6.55581 10.6483 6.55581L9.94327 5.85077C9.80448 5.71199 9.57174 5.72702 9.45561 5.88527C7.40002 8.68679 7.099 11.3958 7.099 11.3958L6.50061 10.7974C6.32693 10.6237 6.03064 10.7007 5.96029 10.936C5.23283 13.369 5.71564 15.8582 5.71564 15.8582L5.71514 15.8586L6.34936 15.7361C6.34525 15.7145 5.97377 13.7188 6.44041 11.6498L6.94955 12.1581Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.09852C13.3141 5.92993 13.224 5.74657 13.0554 5.68887C12.8865 5.63169 12.7031 5.72134 12.6457 5.88989C11.8359 8.26001 10.4981 10.3715 8.9927 12.1831C8.98586 10.4555 9.52113 9.39689 9.5316 9.37665C9.61414 9.21911 9.55367 9.02423 9.39641 8.94118C9.23793 8.85829 9.04348 8.91864 8.96031 9.07622C8.92824 9.13728 8.19051 10.575 8.37992 12.8922C4.65574 17.0669 0.236005 19.4656 0.170849 19.5003C0.0136223 19.5843 -0.0459481 19.7796 0.0381926 19.9369C0.0961615 20.0457 0.207724 20.1076 0.323037 20.1076C0.374404 20.1076 0.426084 20.0955 0.474599 20.0697C0.530303 20.04 3.69996 18.3212 6.9675 15.2554C7.06207 15.2632 7.47437 15.2954 7.91512 15.2954C8.76023 15.2954 10.083 15.2155 11.1436 14.7912C11.3091 14.7249 11.3897 14.5372 11.3236 14.3716C11.2574 14.2062 11.068 14.1263 10.9041 14.1919C9.80613 14.6312 8.34164 14.6618 7.6 14.6443C9.86817 12.3965 12.09 9.51204 13.2564 6.09852Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_5.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.85056C16.7022 7.59411 16.444 7.36591 16.1963 7.46493L15.166 7.87708C16.1394 3.74004 13.6255 1.25496 12.3309 0.283203L11.9303 0.809337C12.295 4.3329 10.8697 6.89856 10.8697 6.89856L9.6861 6.18591C7.63047 8.98743 7.26813 11.76 7.26813 11.76L6.22457 11.0728L5.84278 15.7221L4.19531 17.2344C4.19531 17.2344 9.12215 19.3899 13.862 14.9772C14.0281 14.8226 13.9924 14.5503 13.7895 14.4488L12.2619 13.685C13.7729 12.7865 15.8697 10.511 16.629 7.85056Z" fill="#8EBEFE"/>
|
||||
<path d="M6.94955 12.1889C7.20748 12.4465 7.64893 12.2925 7.69061 11.9303L7.74029 11.499C7.74315 11.4746 8.03272 9.11142 9.75631 6.60701L10.3884 7.23904C10.5932 7.4438 10.9371 7.3956 11.0777 7.14244L11.2124 6.8999C11.2795 6.77951 12.7674 4.0361 12.3306 0.283326C12.2926 0.25481 12.2554 0.227232 12.2196 0.201334C11.9917 0.0362552 11.68 0.217388 11.709 0.49735C12.0737 4.02091 10.6483 6.58658 10.6483 6.58658L9.94327 5.88154C9.80448 5.74275 9.57174 5.75779 9.45561 5.91603C7.40002 8.71756 7.099 11.4266 7.099 11.4266L6.50061 10.8282C6.32693 10.6545 6.03064 10.7314 5.96029 10.9667C5.23283 13.3998 5.71564 15.889 5.71564 15.889L5.71514 15.8894L6.34936 15.7668C6.34525 15.7452 5.97377 13.7496 6.44041 11.6806L6.94955 12.1889Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.12928C13.3141 5.96069 13.224 5.77733 13.0554 5.71964C12.8865 5.66245 12.7031 5.7521 12.6457 5.92065C11.8359 8.29077 10.4981 10.4022 8.9927 12.2139C8.98586 10.4862 9.52113 9.42765 9.5316 9.40741C9.61414 9.24987 9.55367 9.05499 9.39641 8.97194C9.23793 8.88905 9.04348 8.9494 8.96031 9.10698C8.92824 9.16804 8.19051 10.6057 8.37992 12.9229C4.65574 17.0977 0.236005 19.4963 0.170849 19.5311C0.0136223 19.615 -0.0459481 19.8104 0.0381926 19.9676C0.0961615 20.0765 0.207724 20.1384 0.323037 20.1384C0.374404 20.1384 0.426084 20.1263 0.474599 20.1004C0.530303 20.0708 3.69996 18.352 6.9675 15.2862C7.06207 15.2939 7.47437 15.3261 7.91512 15.3261C8.76023 15.3261 10.083 15.2462 11.1436 14.822C11.3091 14.7556 11.3897 14.568 11.3236 14.4024C11.2574 14.2369 11.068 14.1571 10.9041 14.2226C9.80613 14.6619 8.34164 14.6926 7.6 14.6751C9.86817 12.4273 12.09 9.5428 13.2564 6.12928Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_6.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.88132C16.7022 7.62487 16.444 7.39667 16.1963 7.49569L15.166 7.90784C16.1394 3.77081 13.6255 1.28572 12.3309 0.313965L11.9303 0.840098C12.295 4.36366 10.8697 6.92932 10.8697 6.92932L9.6861 6.21667C7.63047 9.01819 7.26813 11.7908 7.26813 11.7908L6.22457 11.1035L5.84278 15.7529L4.19531 17.2651C4.19531 17.2651 9.12215 19.4206 13.862 15.008C14.0281 14.8534 13.9924 14.581 13.7895 14.4796L12.2619 13.7158C13.7729 12.8173 15.8697 10.5417 16.629 7.88132Z" fill="#A0C9FF"/>
|
||||
<path d="M6.94955 12.2196C7.20748 12.4773 7.64893 12.3233 7.69061 11.9611L7.74029 11.5297C7.74315 11.5053 8.03272 9.14218 9.75631 6.63777L10.3884 7.2698C10.5932 7.47457 10.9371 7.42636 11.0777 7.1732L11.2124 6.93066C11.2795 6.81027 12.7674 4.06687 12.3306 0.314088C12.2926 0.285572 12.2554 0.257994 12.2196 0.232095C11.9917 0.0670169 11.68 0.24815 11.709 0.528111C12.0737 4.05167 10.6483 6.61734 10.6483 6.61734L9.94327 5.9123C9.80448 5.77351 9.57174 5.78855 9.45561 5.94679C7.40002 8.74832 7.099 11.4573 7.099 11.4573L6.50061 10.8589C6.32693 10.6853 6.03064 10.7622 5.96029 10.9975C5.23283 13.4305 5.71564 15.9197 5.71564 15.9197L5.71514 15.9202L6.34936 15.7976C6.34525 15.776 5.97377 13.7803 6.44041 11.7113L6.94955 12.2196Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.16005C13.3141 5.99145 13.224 5.80809 13.0554 5.7504C12.8865 5.69321 12.7031 5.78286 12.6457 5.95141C11.8359 8.32153 10.4981 10.433 8.9927 12.2447C8.98586 10.517 9.52113 9.45841 9.5316 9.43818C9.61414 9.28064 9.55367 9.08575 9.39641 9.00271C9.23793 8.91982 9.04348 8.98017 8.96031 9.13774C8.92824 9.1988 8.19051 10.6365 8.37992 12.9537C4.65574 17.1285 0.236005 19.5271 0.170849 19.5618C0.0136223 19.6458 -0.0459481 19.8412 0.0381926 19.9984C0.0961615 20.1073 0.207724 20.1692 0.323037 20.1692C0.374404 20.1692 0.426084 20.157 0.474599 20.1312C0.530303 20.1015 3.69996 18.3827 6.9675 15.3169C7.06207 15.3247 7.47437 15.3569 7.91512 15.3569C8.76023 15.3569 10.083 15.277 11.1436 14.8527C11.3091 14.7864 11.3897 14.5987 11.3236 14.4331C11.2574 14.2677 11.068 14.1878 10.9041 14.2534C9.80613 14.6927 8.34164 14.7233 7.6 14.7058C9.86817 12.4581 12.09 9.57357 13.2564 6.16005Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_7.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.91208C16.7022 7.65563 16.444 7.42743 16.1963 7.52645L15.166 7.9386C16.1394 3.80157 13.6255 1.31649 12.3309 0.344727L11.9303 0.87086C12.295 4.39442 10.8697 6.96009 10.8697 6.96009L9.6861 6.24743C7.63047 9.04896 7.26813 11.8215 7.26813 11.8215L6.22457 11.1343L5.84278 15.7836L4.19531 17.2959C4.19531 17.2959 9.12215 19.4514 13.862 15.0387C14.0281 14.8842 13.9924 14.6118 13.7895 14.5103L12.2619 13.7465C13.7729 12.8481 15.8697 10.5725 16.629 7.91208Z" fill="#CDE2FF"/>
|
||||
<path d="M6.94955 12.2504C7.20748 12.508 7.64893 12.354 7.69061 11.9919L7.74029 11.5605C7.74315 11.5361 8.03272 9.17295 9.75631 6.66853L10.3884 7.30056C10.5932 7.50533 10.9371 7.45712 11.0777 7.20396L11.2124 6.96142C11.2795 6.84103 12.7674 4.09763 12.3306 0.344849C12.2926 0.316334 12.2554 0.288755 12.2196 0.262857C11.9917 0.0977786 11.68 0.278912 11.709 0.558873C12.0737 4.08243 10.6483 6.6481 10.6483 6.6481L9.94327 5.94306C9.80448 5.80427 9.57174 5.81931 9.45561 5.97755C7.40002 8.77908 7.099 11.4881 7.099 11.4881L6.50061 10.8897C6.32693 10.716 6.03064 10.7929 5.96029 11.0283C5.23283 13.4613 5.71564 15.9505 5.71564 15.9505L5.71514 15.9509L6.34936 15.8283C6.34525 15.8067 5.97377 13.8111 6.44041 11.7421L6.94955 12.2504Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.19081C13.3141 6.02221 13.224 5.83885 13.0554 5.78116C12.8865 5.72397 12.7031 5.81362 12.6457 5.98218C11.8359 8.35229 10.4981 10.4637 8.9927 12.2754C8.98586 10.5478 9.52113 9.48917 9.5316 9.46894C9.61414 9.3114 9.55367 9.11651 9.39641 9.03347C9.23793 8.95058 9.04348 9.01093 8.96031 9.16851C8.92824 9.22956 8.19051 10.6673 8.37992 12.9844C4.65574 17.1592 0.236005 19.5579 0.170849 19.5926C0.0136223 19.6766 -0.0459481 19.8719 0.0381926 20.0291C0.0961615 20.138 0.207724 20.1999 0.323037 20.1999C0.374404 20.1999 0.426084 20.1878 0.474599 20.162C0.530303 20.1323 3.69996 18.4135 6.9675 15.3477C7.06207 15.3555 7.47437 15.3877 7.91512 15.3877C8.76023 15.3877 10.083 15.3078 11.1436 14.8835C11.3091 14.8171 11.3897 14.6295 11.3236 14.4639C11.2574 14.2985 11.068 14.2186 10.9041 14.2841C9.80613 14.7234 8.34164 14.7541 7.6 14.7366C9.86817 12.4888 12.09 9.60433 13.2564 6.19081Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_8.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.94333C16.7022 7.68688 16.444 7.45868 16.1963 7.5577L15.166 7.96985C16.1394 3.83282 13.6255 1.34774 12.3309 0.375977L11.9303 0.90211C12.295 4.42567 10.8697 6.99134 10.8697 6.99134L9.6861 6.27868C7.63047 9.08021 7.26813 11.8528 7.26813 11.8528L6.22457 11.1656L5.84278 15.8149L4.19531 17.3271C4.19531 17.3271 9.12215 19.4826 13.862 15.07C14.0281 14.9154 13.9924 14.643 13.7895 14.5416L12.2619 13.7778C13.7729 12.8793 15.8697 10.6037 16.629 7.94333Z" fill="#DCEBFF"/>
|
||||
<path d="M6.94955 12.2814C7.20748 12.539 7.64893 12.385 7.69061 12.0229L7.74029 11.5915C7.74315 11.5671 8.03272 9.20395 9.75631 6.69953L10.3884 7.33157C10.5932 7.53633 10.9371 7.48813 11.0777 7.23496L11.2124 6.99243C11.2795 6.87204 12.7674 4.12863 12.3306 0.375855C12.2926 0.347339 12.2554 0.319761 12.2196 0.293863C11.9917 0.128785 11.68 0.309918 11.709 0.589879C12.0737 4.11344 10.6483 6.67911 10.6483 6.67911L9.94327 5.97407C9.80448 5.83528 9.57174 5.85032 9.45561 6.00856C7.40002 8.81008 7.099 11.5191 7.099 11.5191L6.50061 10.9207C6.32693 10.747 6.03064 10.824 5.96029 11.0593C5.23283 13.4923 5.71564 15.9815 5.71564 15.9815L5.71514 15.9819L6.34936 15.8594C6.34525 15.8377 5.97377 13.8421 6.44041 11.7731L6.94955 12.2814Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.22181C13.3141 6.05322 13.224 5.86986 13.0554 5.81217C12.8865 5.75498 12.7031 5.84463 12.6457 6.01318C11.8359 8.3833 10.4981 10.4947 8.9927 12.3064C8.98586 10.5788 9.52113 9.52018 9.5316 9.49994C9.61414 9.3424 9.55367 9.14752 9.39641 9.06447C9.23793 8.98158 9.04348 9.04193 8.96031 9.19951C8.92824 9.26057 8.19051 10.6983 8.37992 13.0155C4.65574 17.1902 0.236005 19.5889 0.170849 19.6236C0.0136223 19.7076 -0.0459481 19.9029 0.0381926 20.0602C0.0961615 20.169 0.207724 20.2309 0.323037 20.2309C0.374404 20.2309 0.426084 20.2188 0.474599 20.193C0.530303 20.1633 3.69996 18.4445 6.9675 15.3787C7.06207 15.3865 7.47437 15.4187 7.91512 15.4187C8.76023 15.4187 10.083 15.3388 11.1436 14.9145C11.3091 14.8482 11.3897 14.6605 11.3236 14.4949C11.2574 14.3295 11.068 14.2496 10.9041 14.3151C9.80613 14.7544 8.34164 14.7851 7.6 14.7676C9.86817 12.5198 12.09 9.63533 13.2564 6.22181Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
5
assets/icons/sensitivity_feature_9.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="17" height="21" viewBox="0 0 17 21" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.629 7.97409C16.7022 7.71765 16.444 7.48944 16.1963 7.58847L15.166 8.00061C16.1394 3.86358 13.6255 1.3785 12.3309 0.406738L11.9303 0.932872C12.295 4.45643 10.8697 7.0221 10.8697 7.0221L9.6861 6.30944C7.63047 9.11097 7.26813 11.8835 7.26813 11.8835L6.22457 11.1963L5.84278 15.8456L4.19531 17.3579C4.19531 17.3579 9.12215 19.5134 13.862 15.1007C14.0281 14.9462 13.9924 14.6738 13.7895 14.5723L12.2619 13.8086C13.7729 12.9101 15.8697 10.6345 16.629 7.97409Z" fill="#EAF3FF"/>
|
||||
<path d="M6.94955 12.3122C7.20748 12.5698 7.64893 12.4158 7.69061 12.0536L7.74029 11.6223C7.74315 11.5978 8.03272 9.23471 9.75631 6.7303L10.3884 7.36233C10.5932 7.56709 10.9371 7.51889 11.0777 7.26573L11.2124 7.02319C11.2795 6.9028 12.7674 4.15939 12.3306 0.406617C12.2926 0.378101 12.2554 0.350523 12.2196 0.324625C11.9917 0.159546 11.68 0.340679 11.709 0.620641C12.0737 4.1442 10.6483 6.70987 10.6483 6.70987L9.94327 6.00483C9.80448 5.86604 9.57174 5.88108 9.45561 6.03932C7.40002 8.84085 7.099 11.5499 7.099 11.5499L6.50061 10.9515C6.32693 10.7778 6.03064 10.8547 5.96029 11.09C5.23283 13.5231 5.71564 16.0123 5.71564 16.0123L5.71514 16.0127L6.34936 15.8901C6.34525 15.8685 5.97377 13.8728 6.44041 11.8039L6.94955 12.3122Z" fill="#D5EAFF"/>
|
||||
<path d="M13.2564 6.25258C13.3141 6.08398 13.224 5.90062 13.0554 5.84293C12.8865 5.78574 12.7031 5.87539 12.6457 6.04394C11.8359 8.41406 10.4981 10.5255 8.9927 12.3372C8.98586 10.6095 9.52113 9.55094 9.5316 9.5307C9.61414 9.37317 9.55367 9.17828 9.39641 9.09524C9.23793 9.01234 9.04348 9.0727 8.96031 9.23027C8.92824 9.29133 8.19051 10.729 8.37992 13.0462C4.65574 17.221 0.236005 19.6196 0.170849 19.6543C0.0136223 19.7383 -0.0459481 19.9337 0.0381926 20.0909C0.0961615 20.1998 0.207724 20.2617 0.323037 20.2617C0.374404 20.2617 0.426084 20.2495 0.474599 20.2237C0.530303 20.1941 3.69996 18.4752 6.9675 15.4095C7.06207 15.4172 7.47437 15.4494 7.91512 15.4494C8.76023 15.4494 10.083 15.3695 11.1436 14.9452C11.3091 14.8789 11.3897 14.6913 11.3236 14.5257C11.2574 14.3602 11.068 14.2804 10.9041 14.3459C9.80613 14.7852 8.34164 14.8159 7.6 14.7984C9.86817 12.5506 12.09 9.6661 13.2564 6.25258Z" fill="#B8DDFF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
10
assets/icons/space_type.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.61417 2.15625H0.863281V15.9173H1.61417V2.15625Z" fill="#FCD577"/>
|
||||
<path d="M19.6761 20.2188H5.91504V20.9696H19.6761V20.2188Z" fill="#FF6F52"/>
|
||||
<path d="M2.47774 2.53177L1.23889 0.541992L0 2.53177H2.47774Z" fill="#FCD577"/>
|
||||
<path d="M0 15.5425L1.23889 17.5322L2.47774 15.5425H0Z" fill="#FCD577"/>
|
||||
<path d="M19.3018 21.8332L21.2915 20.5944L19.3018 19.3555V21.8332Z" fill="#FF6F52"/>
|
||||
<path d="M6.29154 21.8332L4.30176 20.5944L6.29154 19.3555V21.8332Z" fill="#FF6F52"/>
|
||||
<path d="M21.2917 1.72397V0.541992H4.30176V17.5319H21.2917V2.97543" fill="#CFDCE5"/>
|
||||
<path d="M3.92578 0.166504V17.9073H21.6665V0.166504H3.92578ZM20.9157 10.1588V17.1564H10.1888V14.9644H9.438V17.1564H4.67662V0.917389H9.438V9.40788V10.1588V13.0121H10.1889V10.1588H17.247V9.40788H10.1889V0.917389H20.9157V9.40788H19.0493V10.1588H20.9157Z" fill="#415E72"/>
|
||||
</svg>
|
After Width: | Height: | Size: 935 B |
18
assets/icons/spatial_motion_value.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.8333 1.71851L0.472669 15.9196C-0.702346 17.5301 0.447998 19.7934 2.44166 19.7934H19.225C21.2187 19.7934 22.369 17.5301 21.194 15.9196L10.8333 1.71851Z" fill="#F1FAFF"/>
|
||||
<path d="M21.1931 15.9183L10.8333 1.71851L9.06055 4.14839L17.6475 15.9183C18.8229 17.5294 17.6722 19.7933 15.6779 19.7933H19.2235C21.2178 19.7934 22.3685 17.5294 21.1931 15.9183Z" fill="#C7EEFB"/>
|
||||
<path d="M12.9102 6.05994H8.76926C7.63838 6.05994 6.72168 5.14319 6.72168 4.01236V2.12199C6.72168 2.00515 6.81643 1.9104 6.93327 1.9104H14.7463C14.8631 1.9104 14.9579 2.00515 14.9579 2.12199V4.01236C14.9579 5.14319 14.0411 6.05994 12.9102 6.05994Z" fill="#E0DDE2"/>
|
||||
<path d="M14.748 1.9104H13.5893V2.63688C13.5893 3.77034 12.6704 4.6892 11.537 4.6892H7.40546C7.19886 4.6892 6.99959 4.65835 6.81152 4.6016C7.06628 5.44539 7.84934 6.05998 8.77624 6.05998H12.9078C14.0412 6.05998 14.9601 5.14112 14.9601 4.00766V2.12246C14.9601 2.00536 14.8651 1.9104 14.748 1.9104Z" fill="#C8C1C9"/>
|
||||
<path d="M16.3024 2.66033H5.37858C5.26174 2.66033 5.16699 2.56558 5.16699 2.44874V0.41838C5.16699 0.301538 5.26174 0.206787 5.37858 0.206787H16.3024C16.4192 0.206787 16.514 0.301538 16.514 0.41838V2.44874C16.514 2.56562 16.4193 2.66033 16.3024 2.66033Z" fill="#E0DDE2"/>
|
||||
<path d="M16.3019 0.206787H15.0629V0.997127C15.0629 1.11426 14.9679 1.20923 14.8508 1.20923H5.16699V2.44827C5.16699 2.56541 5.26195 2.66037 5.37909 2.66037H16.3019C16.4191 2.66037 16.514 2.56541 16.514 2.44827V0.418887C16.514 0.30175 16.4191 0.206787 16.3019 0.206787Z" fill="#C8C1C9"/>
|
||||
<path d="M10.841 4.82612C11.141 4.82612 11.3842 4.58293 11.3842 4.28294C11.3842 3.98294 11.141 3.73975 10.841 3.73975C10.541 3.73975 10.2979 3.98294 10.2979 4.28294C10.2979 4.58293 10.541 4.82612 10.841 4.82612Z" fill="#FA2A3B"/>
|
||||
<path d="M10.7136 10.2642C11.2545 10.2642 11.6929 9.82577 11.6929 9.28494C11.6929 8.7441 11.2545 8.30566 10.7136 8.30566C10.1728 8.30566 9.73438 8.7441 9.73438 9.28494C9.73438 9.82577 10.1728 10.2642 10.7136 10.2642Z" fill="#62D8F9"/>
|
||||
<path d="M10.7138 8.30542C10.6442 8.30542 10.5762 8.31283 10.5107 8.32662C10.9541 8.42014 11.2869 8.81349 11.2869 9.28469C11.2869 9.7559 10.9541 10.1492 10.5107 10.2428C10.5763 10.2566 10.6442 10.264 10.7138 10.264C11.2546 10.264 11.6931 9.82551 11.6931 9.28469C11.6931 8.74387 11.2547 8.30542 10.7138 8.30542Z" fill="#00BEF7"/>
|
||||
<path d="M14.1217 13.3878L12.4086 13.1093L11.4778 11.3114C11.3944 11.1233 11.2634 10.8659 10.9741 10.7797L9.88702 10.4535C9.76776 10.4245 9.65308 10.4138 9.4497 10.4457L7.13145 10.9842C6.99264 11.0164 6.86865 11.0944 6.7794 11.2054L5.54163 12.746C5.32157 13.02 5.3652 13.4204 5.63913 13.6405C5.75661 13.7349 5.89736 13.7808 6.03718 13.7808C6.22338 13.7808 6.40789 13.6995 6.53357 13.543L7.6336 12.1739L8.72165 11.9212L8.08273 14.0505C8.07045 14.0863 8.06652 14.1326 8.04896 14.2338L7.68849 16.7926L6.00734 18.2512C5.74192 18.4815 5.71344 18.8834 5.94374 19.1488C6.06959 19.2938 6.24657 19.368 6.4246 19.368C6.57238 19.368 6.72087 19.3168 6.84127 19.2124L8.70227 17.5977C8.81826 17.4971 8.89393 17.358 8.91534 17.2059L9.21009 15.1137L9.24932 15.1255L10.8419 16.9842L11.7847 19.0311C11.892 19.264 12.1223 19.4013 12.363 19.4013C12.452 19.4013 12.5425 19.3825 12.6287 19.3428C12.9479 19.1958 13.0874 18.8179 12.9405 18.4987L11.9606 16.3713C11.936 16.3179 11.9042 16.2682 11.8659 16.2236L10.5325 14.6674L10.9911 13.1391L11.4257 13.9786C11.5183 14.1574 11.69 14.2818 11.8886 14.3141L13.9174 14.6439C13.952 14.6495 13.9864 14.6522 14.0203 14.6522C14.3267 14.6522 14.5968 14.4302 14.6475 14.118C14.7039 13.771 14.4685 13.4442 14.1217 13.3878Z" fill="#62D8F9"/>
|
||||
<path d="M8.99707 10.5509C9.20045 10.519 9.31454 10.5319 9.4338 10.5609L10.5209 10.8871C10.8102 10.9732 10.9411 11.2307 11.0245 11.4187L12.0585 13.427C12.085 13.4785 12.1345 13.5142 12.1917 13.523L14.645 13.9015C14.5979 13.6442 14.3947 13.4322 14.1218 13.3878L12.4088 13.1093L11.4779 11.3114C11.3945 11.1233 11.2635 10.8659 10.9742 10.7797L9.88716 10.4535C9.7679 10.4245 9.65322 10.4138 9.44984 10.4457L8.99707 10.5509Z" fill="#00BEF7"/>
|
||||
<path d="M11.9615 16.371C11.9369 16.3176 11.905 16.2679 11.8667 16.2233L10.5333 14.6671L10.9919 13.1391L10.9337 13.0266C10.8579 12.8802 10.6424 12.9003 10.595 13.0583L10.1416 14.5691C10.1032 14.6971 10.1325 14.8358 10.2194 14.9372L11.4134 16.3307C11.4516 16.3753 11.4835 16.4251 11.5081 16.4784L12.4879 18.6058C12.6191 18.8907 12.5216 19.2219 12.2723 19.3943C12.3025 19.3987 12.3331 19.401 12.3638 19.401C12.4528 19.401 12.5433 19.3822 12.6295 19.3425C12.9487 19.1955 13.0882 18.8176 12.9413 18.4985L11.9615 16.371Z" fill="#00BEF7"/>
|
||||
<path d="M3.47827 18.4408C3.31064 18.4408 3.17032 18.3097 3.16092 18.1403C3.08839 16.8332 4.09282 15.7107 5.39991 15.6381C5.57574 15.6291 5.72551 15.7627 5.7352 15.9381C5.74493 16.1135 5.61065 16.2637 5.4352 16.2734C4.47834 16.3266 3.74305 17.1483 3.79616 18.105C3.8059 18.2805 3.67162 18.4306 3.49617 18.4403C3.4902 18.4406 3.48419 18.4408 3.47827 18.4408Z" fill="#00BEF7"/>
|
||||
<path d="M4.76838 18.4541C4.60076 18.4541 4.46044 18.3231 4.45104 18.1536C4.41524 17.5088 4.91082 16.9549 5.5557 16.9192C5.73081 16.9095 5.88121 17.0438 5.89094 17.2192C5.90068 17.3947 5.76636 17.5447 5.59091 17.5545C5.29634 17.5708 5.06994 17.8238 5.08627 18.1185C5.09601 18.2939 4.96169 18.444 4.78624 18.4537C4.78032 18.454 4.77431 18.4541 4.76838 18.4541Z" fill="#00BEF7"/>
|
||||
<path d="M17.0106 13.6224C17.0076 13.6224 17.0046 13.6223 17.0016 13.6222C16.826 13.6173 16.6876 13.471 16.6925 13.2954C16.7191 12.3374 15.9615 11.5363 15.0036 11.5097C14.828 11.5048 14.6896 11.3584 14.6945 11.1828C14.6994 11.0072 14.8466 10.8688 15.0213 10.8737C16.3299 10.9101 17.365 12.0044 17.3285 13.3131C17.3237 13.4857 17.1822 13.6224 17.0106 13.6224Z" fill="#00BEF7"/>
|
||||
<path d="M15.7216 13.6711C15.7186 13.6711 15.7156 13.6711 15.7126 13.671C15.537 13.6661 15.3986 13.5197 15.4035 13.3441C15.4075 13.2013 15.3555 13.0654 15.2574 12.9615C15.1591 12.8577 15.0263 12.7983 14.8835 12.7943C14.7079 12.7894 14.5695 12.6431 14.5743 12.4675C14.5793 12.2919 14.724 12.1531 14.9012 12.1583C15.214 12.1671 15.5046 12.2971 15.7196 12.5244C15.9346 12.7517 16.0482 13.0491 16.0395 13.3619C16.0346 13.5344 15.8932 13.6711 15.7216 13.6711Z" fill="#00BEF7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
6
assets/icons/spatial_static_value.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.7715 21.8328C16.7205 21.8328 21.5431 16.9829 21.5431 11.0003C21.5431 5.01761 16.7205 0.167725 10.7715 0.167725C4.82259 0.167725 0 5.01761 0 11.0003C0 16.9829 4.82259 21.8328 10.7715 21.8328Z" fill="#B3CEEC"/>
|
||||
<path d="M9.41996 16.2738H7.27293C7.10438 16.2738 6.96777 16.1371 6.96777 15.9686V6.03098C6.96777 5.86243 7.10442 5.72583 7.27293 5.72583H9.41996C9.58851 5.72583 9.72511 5.86247 9.72511 6.03098V15.9686C9.72511 16.1372 9.58851 16.2738 9.41996 16.2738Z" fill="#5F99D7"/>
|
||||
<path d="M14.3946 16.2738H12.2475C12.079 16.2738 11.9424 16.1371 11.9424 15.9686V6.03098C11.9424 5.86243 12.079 5.72583 12.2475 5.72583H14.3946C14.5631 5.72583 14.6997 5.86247 14.6997 6.03098V15.9686C14.6997 16.1372 14.5631 16.2738 14.3946 16.2738Z" fill="#5F99D7"/>
|
||||
<path d="M10.8332 0.166748C10.584 0.166748 10.3369 0.175931 10.0918 0.19252C15.7289 0.573633 20.1837 5.26629 20.1837 11.0001C20.1837 16.7339 15.7289 21.4265 10.0918 21.8076C10.3369 21.8242 10.584 21.8334 10.8332 21.8334C16.8163 21.8334 21.6666 16.9832 21.6666 11.0001C21.6666 5.01699 16.8163 0.166748 10.8332 0.166748Z" fill="#98BCE5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
6
assets/icons/sports_para.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.8334 21.8332C7.93969 21.8332 5.21921 20.7063 3.17302 18.6601C1.12688 16.614 0 13.8935 0 10.9999C0 8.10619 1.12688 5.38572 3.17298 3.33953C5.21921 1.29338 7.93961 0.166504 10.8334 0.166504C13.727 0.166504 16.4475 1.29338 18.4936 3.33953C20.5398 5.38572 21.6667 8.10615 21.6667 10.9999C21.6667 13.8936 20.5397 16.614 18.4936 18.6602C16.4475 20.7064 13.727 21.8332 10.8334 21.8332ZM10.8334 3.12028C6.48853 3.12028 2.95378 6.65504 2.95378 10.9999C2.95378 15.3447 6.48853 18.8794 10.8334 18.8794C15.1782 18.8794 18.713 15.3447 18.713 10.9999C18.713 6.65508 15.1781 3.12028 10.8334 3.12028Z" fill="#023DFE" fill-opacity="0.6"/>
|
||||
<g opacity="0.1">
|
||||
<path d="M5.14215 18.6601C3.09605 16.614 1.96917 13.8935 1.96917 10.9998C1.96917 8.10615 3.09605 5.38567 5.14215 3.33957C6.95445 1.52727 9.29585 0.436914 11.8179 0.211149C11.4924 0.18195 11.1641 0.166504 10.8334 0.166504C7.93969 0.166504 5.21921 1.29338 3.17302 3.33953C1.12688 5.38563 0 8.10615 0 10.9998C0 13.8934 1.12688 16.614 3.17298 18.6601C5.21913 20.7062 7.93961 21.8332 10.8333 21.8332C11.164 21.8332 11.4924 21.8177 11.8179 21.7886C9.29585 21.5628 6.95449 20.4724 5.14215 18.6601Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
26
lib/common/widgets/empty_search_result_widget.dart
Normal file
@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class EmptySearchResultWidget extends StatelessWidget {
|
||||
const EmptySearchResultWidget({
|
||||
this.message = 'No results found',
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String message;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
53
lib/common/widgets/sidebar_communities_list.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class SidebarCommunitiesList extends StatelessWidget {
|
||||
const SidebarCommunitiesList({
|
||||
required this.communities,
|
||||
required this.itemBuilder,
|
||||
required this.scrollController,
|
||||
required this.onScrollToEnd,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<CommunityModel> communities;
|
||||
final Widget Function(BuildContext context, int index) itemBuilder;
|
||||
final ScrollController scrollController;
|
||||
final void Function() onScrollToEnd;
|
||||
|
||||
bool _onNotification(ScrollEndNotification notification) {
|
||||
final hasReachedEnd = notification.metrics.extentAfter == 0;
|
||||
if (hasReachedEnd) {
|
||||
onScrollToEnd.call();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: context.screenWidth * 0.5,
|
||||
child: Scrollbar(
|
||||
scrollbarOrientation: ScrollbarOrientation.left,
|
||||
thumbVisibility: true,
|
||||
controller: scrollController,
|
||||
child: NotificationListener<ScrollEndNotification>(
|
||||
onNotification: _onNotification,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
itemCount: communities.length,
|
||||
controller: scrollController,
|
||||
itemBuilder: itemBuilder,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -16,12 +16,12 @@ import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.da
|
||||
import 'package:syncrow_web/services/locator.dart';
|
||||
import 'package:syncrow_web/utils/app_routes.dart';
|
||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
import 'package:syncrow_web/utils/theme/theme.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
try {
|
||||
const environment =
|
||||
String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||
await dotenv.load(fileName: '.env.$environment');
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(
|
||||
@ -33,9 +33,7 @@ Future<void> main() async {
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
MyApp({
|
||||
super.key,
|
||||
});
|
||||
MyApp({super.key});
|
||||
|
||||
final GoRouter _router = GoRouter(
|
||||
initialLocation: RoutesConst.auth,
|
||||
@ -56,11 +54,10 @@ class MyApp extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<CreateRoutineBloc>(
|
||||
BlocProvider<CreateRoutineBloc>(
|
||||
create: (context) => CreateRoutineBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||
BlocProvider<VisitorPasswordBloc>(
|
||||
create: (context) => VisitorPasswordBloc(),
|
||||
),
|
||||
@ -81,6 +78,8 @@ class MyApp extends StatelessWidget {
|
||||
PointerDeviceKind.unknown,
|
||||
},
|
||||
),
|
||||
key: NavigationService.navigatorKey,
|
||||
// scaffoldMessengerKey: NavigationService.snackbarKey,
|
||||
theme: myTheme,
|
||||
routerConfig: _router,
|
||||
));
|
||||
|
87
lib/main_staging.dart
Normal file
@ -0,0 +1,87 @@
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/firebase_options_prod.dart';
|
||||
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
||||
import 'package:syncrow_web/services/locator.dart';
|
||||
import 'package:syncrow_web/utils/app_routes.dart';
|
||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
import 'package:syncrow_web/utils/theme/theme.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
try {
|
||||
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
|
||||
await dotenv.load(fileName: '.env.$environment');
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptionsStaging.currentPlatform,
|
||||
);
|
||||
initialSetup();
|
||||
} catch (_) {}
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
MyApp({super.key});
|
||||
|
||||
final GoRouter _router = GoRouter(
|
||||
initialLocation: RoutesConst.auth,
|
||||
routes: AppRoutes.getRoutes(),
|
||||
redirect: (context, state) async {
|
||||
String checkToken = await AuthBloc.getTokenAndValidate();
|
||||
final loggedIn = checkToken == 'Success';
|
||||
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
||||
|
||||
if (!loggedIn && !goingToLogin) return RoutesConst.auth;
|
||||
if (loggedIn && goingToLogin) return RoutesConst.home;
|
||||
|
||||
return null;
|
||||
},
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<CreateRoutineBloc>(
|
||||
create: (context) => CreateRoutineBloc(),
|
||||
),
|
||||
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||
BlocProvider<VisitorPasswordBloc>(
|
||||
create: (context) => VisitorPasswordBloc(),
|
||||
),
|
||||
BlocProvider<RoutineBloc>(
|
||||
create: (context) => RoutineBloc(),
|
||||
),
|
||||
BlocProvider<SpaceTreeBloc>(
|
||||
create: (context) => SpaceTreeBloc()..add(InitialEvent()),
|
||||
),
|
||||
],
|
||||
child: MaterialApp.router(
|
||||
debugShowCheckedModeBanner: false,
|
||||
scrollBehavior: const MaterialScrollBehavior().copyWith(
|
||||
dragDevices: {
|
||||
PointerDeviceKind.mouse,
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.stylus,
|
||||
PointerDeviceKind.unknown,
|
||||
},
|
||||
),
|
||||
key: NavigationService.navigatorKey,
|
||||
// scaffoldMessengerKey: NavigationService.snackbarKey,
|
||||
theme: myTheme,
|
||||
routerConfig: _router,
|
||||
));
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/auth/model/region_model.dart';
|
||||
import 'package:syncrow_web/pages/auth/model/token.dart';
|
||||
import 'package:syncrow_web/pages/auth/model/user_model.dart';
|
||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
||||
import 'package:syncrow_web/services/auth_api.dart';
|
||||
@ -432,9 +433,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
static Future<void> logout(BuildContext context) async {
|
||||
final storage = FlutterSecureStorage();
|
||||
ProjectManager.clearProjectUUID();
|
||||
const storage = FlutterSecureStorage();
|
||||
context.read<SpaceTreeBloc>().add(ClearAllData());
|
||||
storage.deleteAll();
|
||||
user = null;
|
||||
context.read<HomeBloc>().user = null;
|
||||
await Future.wait<void>([
|
||||
ProjectManager.clearProjectUUID(),
|
||||
storage.deleteAll(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -201,20 +201,17 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
!state.isButtonEnabled &&
|
||||
state.remainingTime != 1
|
||||
? null
|
||||
: () {
|
||||
:
|
||||
() {
|
||||
if (forgetBloc
|
||||
.forgetEmailKey.currentState!
|
||||
.validate() ||
|
||||
forgetBloc
|
||||
.forgetRegionKey.currentState!
|
||||
.validate()) {
|
||||
if (forgetBloc
|
||||
.forgetRegionKey.currentState!
|
||||
.validate()) {
|
||||
forgetBloc.add(StartTimerEvent());
|
||||
}
|
||||
.forgetEmailKey
|
||||
.currentState!
|
||||
.validate()) {
|
||||
forgetBloc.add(
|
||||
StartTimerEvent());
|
||||
}
|
||||
},
|
||||
|
||||
child: Text(
|
||||
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
||||
style: TextStyle(
|
||||
|
@ -55,12 +55,12 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
final isSmallScreen = isSmallScreenSize(context);
|
||||
final isMediumScreen = isMediumScreenSize(context);
|
||||
Size size = MediaQuery.of(context).size;
|
||||
late ScrollController _scrollController;
|
||||
_scrollController = ScrollController();
|
||||
late ScrollController scrollController;
|
||||
scrollController = ScrollController();
|
||||
|
||||
void _scrollToCenter() {
|
||||
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
||||
_scrollController.animateTo(
|
||||
void scrollToCenter() {
|
||||
final double middlePosition = scrollController.position.maxScrollExtent / 2;
|
||||
scrollController.animateTo(
|
||||
middlePosition,
|
||||
duration: const Duration(seconds: 1),
|
||||
curve: Curves.easeInOut,
|
||||
@ -68,7 +68,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_scrollToCenter();
|
||||
scrollToCenter();
|
||||
});
|
||||
|
||||
return Stack(
|
||||
@ -76,7 +76,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
FirstLayer(
|
||||
second: Center(
|
||||
child: ListView(
|
||||
controller: _scrollController,
|
||||
controller: scrollController,
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Container(
|
||||
@ -199,7 +199,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
width: size.width * 0.9,
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton2<String>(
|
||||
style: TextStyle(color: Colors.black),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
isExpanded: true,
|
||||
hint: Text(
|
||||
'Select your region/country',
|
||||
@ -336,6 +336,16 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
obscureText: loginBloc.obscureText,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
controller: loginBloc.loginPasswordController,
|
||||
onFieldSubmitted: (value) {
|
||||
if (loginBloc.loginFormKey.currentState!.validate()) {
|
||||
loginBloc.add(LoginButtonPressed(
|
||||
username: loginBloc.loginEmailController.text,
|
||||
password: value,
|
||||
));
|
||||
} else {
|
||||
loginBloc.add(ChangeValidateEvent());
|
||||
}
|
||||
},
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
hintText: 'At least 8 characters',
|
||||
hintStyle: Theme.of(context)
|
||||
@ -393,7 +403,7 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
||||
Transform.scale(
|
||||
scale: 1.2,
|
||||
child: Checkbox(
|
||||
fillColor: MaterialStateProperty.all<Color>(Colors.white),
|
||||
fillColor: WidgetStateProperty.all<Color>(Colors.white),
|
||||
activeColor: Colors.white,
|
||||
value: loginBloc.isChecked,
|
||||
checkColor: Colors.black,
|
||||
|
@ -13,6 +13,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
late AcStatusModel deviceStatus;
|
||||
final String deviceId;
|
||||
Timer? _timer;
|
||||
Timer? _countdownTimer;
|
||||
|
||||
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
|
||||
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
|
||||
@ -21,7 +22,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
on<AcBatchControlEvent>(_onAcBatchControl);
|
||||
on<AcFactoryResetEvent>(_onFactoryReset);
|
||||
on<AcStatusUpdated>(_onAcStatusUpdated);
|
||||
on<OnClose>(_onClose);
|
||||
on<IncreaseTimeEvent>(_handleIncreaseTime);
|
||||
on<DecreaseTimeEvent>(_handleDecreaseTime);
|
||||
on<UpdateTimerEvent>(_handleUpdateTimer);
|
||||
on<ToggleScheduleEvent>(_handleToggleTimer);
|
||||
on<ApiCountdownValueEvent>(_handleApiCountdownValue);
|
||||
}
|
||||
bool timerActive = false;
|
||||
int scheduledHours = 0;
|
||||
int scheduledMinutes = 0;
|
||||
|
||||
FutureOr<void> _onFetchAcStatus(
|
||||
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
|
||||
@ -30,8 +40,23 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
final status =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = AcStatusModel.fromJson(event.deviceId, status.status);
|
||||
if (deviceStatus.countdown1 != 0) {
|
||||
// Convert API value to minutes
|
||||
final totalMinutes = deviceStatus.countdown1 * 6;
|
||||
scheduledHours = totalMinutes ~/ 60;
|
||||
scheduledMinutes = totalMinutes % 60;
|
||||
timerActive = true;
|
||||
_startCountdownTimer(emit);
|
||||
}
|
||||
|
||||
emit(ACStatusLoaded(
|
||||
status: deviceStatus,
|
||||
scheduledHours: scheduledHours,
|
||||
scheduledMinutes: scheduledMinutes,
|
||||
isTimerActive: timerActive,
|
||||
));
|
||||
|
||||
_listenToChanges(event.deviceId);
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(AcsFailedState(error: e.toString()));
|
||||
}
|
||||
@ -70,31 +95,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
|
||||
void _onAcStatusUpdated(AcStatusUpdated event, Emitter<AcsState> emit) {
|
||||
deviceStatus = event.deviceStatus;
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
}
|
||||
|
||||
// Future<void> testFirebaseConnection() async {
|
||||
// // Reference to a test node in your database
|
||||
// final testRef = FirebaseDatabase.instance.ref("test");
|
||||
|
||||
// // Write a test value
|
||||
// await testRef.set("Hello, Firebase!");
|
||||
|
||||
// // Listen for changes on the test node
|
||||
// testRef.onValue.listen((DatabaseEvent event) {
|
||||
// final data = event.snapshot.value;
|
||||
// print("Data from Firebase: $data");
|
||||
// // If you see "Hello, Firebase!" printed in your console, it means the connection works.
|
||||
// });
|
||||
// }
|
||||
|
||||
FutureOr<void> _onAcControl(
|
||||
AcControlEvent event, Emitter<AcsState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value, emit);
|
||||
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
isBatch: false,
|
||||
@ -151,7 +161,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
void _revertValueAndEmit(
|
||||
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
|
||||
_updateLocalValue(code, oldValue, emit);
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
|
||||
@ -184,11 +194,16 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(childLock: value);
|
||||
}
|
||||
|
||||
case 'countdown_time':
|
||||
if (value is int) {
|
||||
deviceStatus = deviceStatus.copyWith(countdown1: value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
}
|
||||
|
||||
dynamic _getValueByCode(String code) {
|
||||
@ -203,6 +218,8 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
return deviceStatus.fanSpeedsString;
|
||||
case 'child_lock':
|
||||
return deviceStatus.childLock;
|
||||
case 'countdown_time':
|
||||
return deviceStatus.countdown1;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -216,7 +233,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
deviceStatus =
|
||||
AcStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
} catch (e) {
|
||||
emit(AcsFailedState(error: e.toString()));
|
||||
}
|
||||
@ -228,7 +245,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
|
||||
_updateLocalValue(event.code, event.value, emit);
|
||||
|
||||
emit(ACStatusLoaded(deviceStatus));
|
||||
emit(ACStatusLoaded(status: deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
isBatch: true,
|
||||
@ -257,4 +274,144 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
||||
emit(AcsFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _onClose(OnClose event, Emitter<AcsState> emit) {
|
||||
_countdownTimer?.cancel();
|
||||
_timer?.cancel();
|
||||
}
|
||||
|
||||
void _handleIncreaseTime(IncreaseTimeEvent event, Emitter<AcsState> emit) {
|
||||
if (state is! ACStatusLoaded) return;
|
||||
final currentState = state as ACStatusLoaded;
|
||||
int newHours = scheduledHours;
|
||||
int newMinutes = scheduledMinutes + 30;
|
||||
newHours += newMinutes ~/ 60;
|
||||
newMinutes = newMinutes % 60;
|
||||
if (newHours > 23) {
|
||||
newHours = 23;
|
||||
newMinutes = 59;
|
||||
}
|
||||
scheduledHours = newHours;
|
||||
scheduledMinutes = newMinutes;
|
||||
|
||||
emit(currentState.copyWith(
|
||||
scheduledHours: scheduledHours,
|
||||
scheduledMinutes: scheduledMinutes,
|
||||
));
|
||||
}
|
||||
|
||||
void _handleDecreaseTime(DecreaseTimeEvent event, Emitter<AcsState> emit) {
|
||||
if (state is! ACStatusLoaded) return;
|
||||
final currentState = state as ACStatusLoaded;
|
||||
int totalMinutes = (scheduledHours * 60) + scheduledMinutes;
|
||||
totalMinutes = (totalMinutes - 30).clamp(0, 1440);
|
||||
scheduledHours = totalMinutes ~/ 60;
|
||||
scheduledMinutes = totalMinutes % 60;
|
||||
|
||||
emit(currentState.copyWith(
|
||||
scheduledHours: scheduledHours,
|
||||
scheduledMinutes: scheduledMinutes,
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> _handleToggleTimer(
|
||||
ToggleScheduleEvent event, Emitter<AcsState> emit) async {
|
||||
if (state is! ACStatusLoaded) return;
|
||||
final currentState = state as ACStatusLoaded;
|
||||
|
||||
timerActive = !timerActive;
|
||||
|
||||
if (timerActive) {
|
||||
final totalMinutes = scheduledHours * 60 + scheduledMinutes;
|
||||
if (totalMinutes <= 0) {
|
||||
timerActive = false;
|
||||
emit(currentState.copyWith(isTimerActive: timerActive));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final scaledValue = totalMinutes ~/ 6;
|
||||
await _runDebounce(
|
||||
isBatch: false,
|
||||
deviceId: deviceId,
|
||||
code: 'countdown_time',
|
||||
value: scaledValue,
|
||||
oldValue: scaledValue,
|
||||
emit: emit,
|
||||
);
|
||||
_startCountdownTimer(emit);
|
||||
emit(currentState.copyWith(isTimerActive: timerActive));
|
||||
} catch (e) {
|
||||
timerActive = false;
|
||||
emit(AcsFailedState(error: e.toString()));
|
||||
}
|
||||
} else {
|
||||
await _runDebounce(
|
||||
isBatch: false,
|
||||
deviceId: deviceId,
|
||||
code: 'countdown_time',
|
||||
value: 0,
|
||||
oldValue: 0,
|
||||
emit: emit,
|
||||
);
|
||||
_countdownTimer?.cancel();
|
||||
scheduledHours = 0;
|
||||
scheduledMinutes = 0;
|
||||
emit(currentState.copyWith(
|
||||
isTimerActive: timerActive,
|
||||
scheduledHours: 0,
|
||||
scheduledMinutes: 0,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _startCountdownTimer(Emitter<AcsState> emit) {
|
||||
_countdownTimer?.cancel();
|
||||
int totalSeconds = (scheduledHours * 3600) + (scheduledMinutes * 60);
|
||||
|
||||
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (totalSeconds > 0) {
|
||||
totalSeconds--;
|
||||
scheduledHours = totalSeconds ~/ 3600;
|
||||
scheduledMinutes = (totalSeconds % 3600) ~/ 60;
|
||||
add(UpdateTimerEvent());
|
||||
} else {
|
||||
_countdownTimer?.cancel();
|
||||
timerActive = false;
|
||||
scheduledHours = 0;
|
||||
scheduledMinutes = 0;
|
||||
add(TimerCompletedEvent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _handleUpdateTimer(UpdateTimerEvent event, Emitter<AcsState> emit) {
|
||||
if (state is ACStatusLoaded) {
|
||||
final currentState = state as ACStatusLoaded;
|
||||
emit(currentState.copyWith(
|
||||
scheduledHours: scheduledHours,
|
||||
scheduledMinutes: scheduledMinutes,
|
||||
isTimerActive: timerActive,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _handleApiCountdownValue(
|
||||
ApiCountdownValueEvent event, Emitter<AcsState> emit) {
|
||||
if (state is ACStatusLoaded) {
|
||||
final totalMinutes = event.apiValue * 6;
|
||||
final scheduledHours = totalMinutes ~/ 60;
|
||||
scheduledMinutes = totalMinutes % 60;
|
||||
_startCountdownTimer(
|
||||
emit,
|
||||
);
|
||||
add(UpdateTimerEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
add(OnClose());
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ sealed class AcsEvent extends Equatable {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class AcUpdated extends AcsEvent {}
|
||||
|
||||
class AcFetchDeviceStatusEvent extends AcsEvent {
|
||||
@ -18,10 +19,12 @@ class AcFetchDeviceStatusEvent extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [deviceId];
|
||||
}
|
||||
|
||||
class AcStatusUpdated extends AcsEvent {
|
||||
final AcStatusModel deviceStatus;
|
||||
AcStatusUpdated(this.deviceStatus);
|
||||
}
|
||||
|
||||
class AcFetchBatchStatusEvent extends AcsEvent {
|
||||
final List<String> devicesIds;
|
||||
|
||||
@ -73,3 +76,30 @@ class AcFactoryResetEvent extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [deviceId, factoryResetModel];
|
||||
}
|
||||
|
||||
|
||||
|
||||
class OnClose extends AcsEvent {}
|
||||
|
||||
class IncreaseTimeEvent extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class DecreaseTimeEvent extends AcsEvent {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class ToggleScheduleEvent extends AcsEvent {}
|
||||
|
||||
class TimerCompletedEvent extends AcsEvent {}
|
||||
|
||||
class UpdateTimerEvent extends AcsEvent {
|
||||
}
|
||||
|
||||
class ApiCountdownValueEvent extends AcsEvent {
|
||||
final int apiValue;
|
||||
|
||||
const ApiCountdownValueEvent(this.apiValue);
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
|
||||
|
||||
abstract class AcsState extends Equatable {
|
||||
const AcsState();
|
||||
final bool isTimerActive;
|
||||
|
||||
const AcsState({this.isTimerActive = false});
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
@ -15,8 +16,30 @@ class AcsLoadingState extends AcsState {}
|
||||
class ACStatusLoaded extends AcsState {
|
||||
final AcStatusModel status;
|
||||
final DateTime timestamp;
|
||||
final int scheduledHours;
|
||||
final int scheduledMinutes;
|
||||
final bool isTimerActive;
|
||||
|
||||
ACStatusLoaded(this.status) : timestamp = DateTime.now();
|
||||
ACStatusLoaded({
|
||||
required this.status,
|
||||
this.scheduledHours = 0,
|
||||
this.scheduledMinutes = 0,
|
||||
this.isTimerActive = false,
|
||||
}) : timestamp = DateTime.now();
|
||||
ACStatusLoaded copyWith({
|
||||
AcStatusModel? status,
|
||||
int? scheduledHours,
|
||||
int? scheduledMinutes,
|
||||
bool? isTimerActive,
|
||||
int? remainingTime,
|
||||
}) {
|
||||
return ACStatusLoaded(
|
||||
status: status ?? this.status,
|
||||
scheduledHours: scheduledHours ?? this.scheduledHours,
|
||||
scheduledMinutes: scheduledMinutes ?? this.scheduledMinutes,
|
||||
isTimerActive: isTimerActive ?? this.isTimerActive,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [status, timestamp];
|
||||
@ -40,3 +63,14 @@ class AcsFailedState extends AcsState {
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class TimerRunInProgress extends AcsState {
|
||||
final int remainingTime;
|
||||
|
||||
const TimerRunInProgress(this.remainingTime);
|
||||
|
||||
@override
|
||||
List<Object> get props => [remainingTime];
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,6 +11,7 @@ class AcStatusModel {
|
||||
final bool childLock;
|
||||
final TempModes acMode;
|
||||
final FanSpeeds acFanSpeed;
|
||||
final int countdown1;
|
||||
|
||||
AcStatusModel({
|
||||
required this.uuid,
|
||||
@ -18,6 +19,7 @@ class AcStatusModel {
|
||||
required this.modeString,
|
||||
required this.tempSet,
|
||||
required this.currentTemp,
|
||||
required this.countdown1,
|
||||
required this.fanSpeedsString,
|
||||
required this.childLock,
|
||||
}) : acMode = getACMode(modeString),
|
||||
@ -30,6 +32,7 @@ class AcStatusModel {
|
||||
late int currentTemp;
|
||||
late String fanSpeeds;
|
||||
late bool childLock;
|
||||
late int _countdown1 = 0;
|
||||
|
||||
for (var status in jsonList) {
|
||||
switch (status.code) {
|
||||
@ -51,6 +54,9 @@ class AcStatusModel {
|
||||
case 'child_lock':
|
||||
childLock = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_time':
|
||||
_countdown1 = status.value ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,6 +68,7 @@ class AcStatusModel {
|
||||
currentTemp: currentTemp,
|
||||
fanSpeedsString: fanSpeeds,
|
||||
childLock: childLock,
|
||||
countdown1: _countdown1,
|
||||
);
|
||||
}
|
||||
|
||||
@ -73,6 +80,7 @@ class AcStatusModel {
|
||||
int? currentTemp,
|
||||
String? fanSpeedsString,
|
||||
bool? childLock,
|
||||
int? countdown1,
|
||||
}) {
|
||||
return AcStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
@ -82,6 +90,7 @@ class AcStatusModel {
|
||||
currentTemp: currentTemp ?? this.currentTemp,
|
||||
fanSpeedsString: fanSpeedsString ?? this.fanSpeedsString,
|
||||
childLock: childLock ?? this.childLock,
|
||||
countdown1: countdown1 ?? this.countdown1,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,10 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const AcDeviceControlsView({super.key, required this.device});
|
||||
const AcDeviceControlsView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
|
||||
@ -23,11 +22,13 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
|
||||
return BlocProvider(
|
||||
create: (context) => AcBloc(deviceId: device.uuid!)
|
||||
..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
||||
child: BlocBuilder<AcBloc, AcsState>(
|
||||
builder: (context, state) {
|
||||
final acBloc = BlocProvider.of<AcBloc>(context);
|
||||
if (state is ACStatusLoaded) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
@ -78,56 +79,101 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
ToggleWidget(
|
||||
label: '',
|
||||
labelWidget: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
labelWidget: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
padding: const EdgeInsets.all(0),
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
Icons.remove,
|
||||
size: 28,
|
||||
color: ColorsManager.greyColor,
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: const ShapeDecoration(
|
||||
color: ColorsManager.primaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'06',
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'h',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
Text(
|
||||
'30',
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text('m',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor)),
|
||||
IconButton(
|
||||
padding: const EdgeInsets.all(0),
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
Icons.add,
|
||||
size: 28,
|
||||
color: ColorsManager.greyColor,
|
||||
Center(
|
||||
child: SizedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (acBloc.timerActive == false) {
|
||||
context
|
||||
.read<AcBloc>()
|
||||
.add(DecreaseTimeEvent());
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.remove,
|
||||
color: ColorsManager.greyColor),
|
||||
),
|
||||
Text(
|
||||
acBloc.scheduledHours
|
||||
.toString()
|
||||
.padLeft(2, '0'),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'h',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
acBloc.scheduledMinutes
|
||||
.toString()
|
||||
.padLeft(2, '0'),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleLarge!
|
||||
.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'm',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
if (acBloc.timerActive == false) {
|
||||
context
|
||||
.read<AcBloc>()
|
||||
.add(IncreaseTimeEvent());
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.add,
|
||||
color: ColorsManager.greyColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
value: false,
|
||||
value: acBloc.timerActive,
|
||||
code: 'ac_schedule',
|
||||
deviceId: device.uuid!,
|
||||
icon: Assets.acSchedule,
|
||||
onChange: (value) {},
|
||||
onChange: (value) {
|
||||
context.read<AcBloc>().add(ToggleScheduleEvent());
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
deviceId: device.uuid!,
|
||||
|
@ -60,7 +60,15 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(CurrentTemp oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.tempSet != widget.tempSet) {
|
||||
setState(() {
|
||||
_adjustedValue = _initialAdjustedValue(widget.tempSet);
|
||||
});
|
||||
}
|
||||
}
|
||||
@override
|
||||
void dispose() {
|
||||
_debounce?.cancel();
|
||||
|
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st
|
||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/views/flush_mounted_presence_sensor_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart';
|
||||
@ -104,6 +105,9 @@ mixin RouteControlsBasedCode {
|
||||
);
|
||||
case 'SOS':
|
||||
return SosDeviceControlsView(device: device);
|
||||
|
||||
case 'NCPS':
|
||||
return FlushMountedPresenceSensorControlView(device: device);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
class DeviceSubSpace {
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
String? subspaceName;
|
||||
bool? disabled;
|
||||
|
||||
DeviceSubSpace({this.id, this.createdAt, this.updatedAt, this.subspaceName, this.disabled});
|
||||
|
||||
DeviceSubSpace.fromJson(Map<String, dynamic> json) {
|
||||
id = json['uuid']?.toString() ?? '';
|
||||
createdAt = json['createdAt']?.toString() ?? '';
|
||||
updatedAt = json['updatedAt']?.toString() ?? '';
|
||||
subspaceName = json['subspaceName']?.toString() ?? '';
|
||||
subspaceName = json['subspaceName']?.toString() ?? '';
|
||||
disabled = json['disabled'] ?? false;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
class DeviceTagModel {
|
||||
String? id;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
String? name;
|
||||
|
||||
DeviceTagModel({
|
||||
this.id,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.name,
|
||||
});
|
||||
|
||||
DeviceTagModel.fromJson(Map<String, dynamic> json) {
|
||||
id = json['uuid']?.toString() ?? '';
|
||||
createdAt = json['createdAt']?.toString() ?? '';
|
||||
updatedAt = json['updatedAt']?.toString() ?? '';
|
||||
name = json['name']?.toString() ?? '';
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_community.model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_space_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sub_space.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_subspace.model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_tag_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||
@ -10,6 +12,7 @@ import 'package:syncrow_web/pages/routines/models/gang_switches/three_gang_switc
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/two_gang_switch/two_gang_switch.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gateway.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/enum/device_types.dart';
|
||||
|
||||
@ -78,38 +81,41 @@ class AllDevicesModel {
|
||||
int? batteryLevel;
|
||||
String? productName;
|
||||
List<DeviceSpaceModel>? spaces;
|
||||
List<DeviceTagModel>? deviceTags;
|
||||
DeviceSubSpace? deviceSubSpace;
|
||||
|
||||
AllDevicesModel({
|
||||
this.room,
|
||||
this.subspace,
|
||||
this.unit,
|
||||
this.community,
|
||||
this.productUuid,
|
||||
this.productType,
|
||||
this.permissionType,
|
||||
this.activeTime,
|
||||
this.category,
|
||||
this.categoryName,
|
||||
this.createTime,
|
||||
this.gatewayId,
|
||||
this.icon,
|
||||
this.ip,
|
||||
this.lat,
|
||||
this.localKey,
|
||||
this.lon,
|
||||
this.model,
|
||||
this.name,
|
||||
this.nodeId,
|
||||
this.online,
|
||||
this.ownerId,
|
||||
this.sub,
|
||||
this.timeZone,
|
||||
this.updateTime,
|
||||
this.uuid,
|
||||
this.batteryLevel,
|
||||
this.productName,
|
||||
this.spaces,
|
||||
});
|
||||
AllDevicesModel(
|
||||
{this.room,
|
||||
this.subspace,
|
||||
this.unit,
|
||||
this.community,
|
||||
this.productUuid,
|
||||
this.productType,
|
||||
this.permissionType,
|
||||
this.activeTime,
|
||||
this.category,
|
||||
this.categoryName,
|
||||
this.createTime,
|
||||
this.gatewayId,
|
||||
this.icon,
|
||||
this.ip,
|
||||
this.lat,
|
||||
this.localKey,
|
||||
this.lon,
|
||||
this.model,
|
||||
this.name,
|
||||
this.nodeId,
|
||||
this.online,
|
||||
this.ownerId,
|
||||
this.sub,
|
||||
this.timeZone,
|
||||
this.updateTime,
|
||||
this.uuid,
|
||||
this.batteryLevel,
|
||||
this.productName,
|
||||
this.spaces,
|
||||
this.deviceTags,
|
||||
this.deviceSubSpace});
|
||||
|
||||
AllDevicesModel.fromJson(Map<String, dynamic> json) {
|
||||
room = (json['room'] != null && (json['room'] is Map))
|
||||
@ -147,12 +153,15 @@ class AllDevicesModel {
|
||||
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
|
||||
uuid = json['uuid']?.toString();
|
||||
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
|
||||
|
||||
productName = json['productName']?.toString();
|
||||
deviceTags = json['deviceTag'] != null && json['deviceTag'] is List
|
||||
? (json['deviceTag'] as List).map((tag) => DeviceTagModel.fromJson(tag)).toList()
|
||||
: [];
|
||||
deviceSubSpace = json['subspace'] != null
|
||||
? DeviceSubSpace.fromJson(json['subspace'])
|
||||
: DeviceSubSpace(subspaceName: '');
|
||||
if (json['spaces'] != null && json['spaces'] is List) {
|
||||
spaces = (json['spaces'] as List)
|
||||
.map((space) => DeviceSpaceModel.fromJson(space))
|
||||
.toList();
|
||||
spaces = (json['spaces'] as List).map((space) => DeviceSpaceModel.fromJson(space)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,8 +209,7 @@ SOS
|
||||
String tempIcon = '';
|
||||
if (type == DeviceType.LightBulb) {
|
||||
tempIcon = Assets.lightBulb;
|
||||
} else if (type == DeviceType.CeilingSensor ||
|
||||
type == DeviceType.WallSensor) {
|
||||
} else if (type == DeviceType.CeilingSensor || type == DeviceType.WallSensor) {
|
||||
tempIcon = Assets.sensors;
|
||||
} else if (type == DeviceType.AC) {
|
||||
tempIcon = Assets.ac;
|
||||
@ -231,8 +239,6 @@ SOS
|
||||
// tempIcon = Assets.gang3touch;
|
||||
} else if (type == DeviceType.WaterLeak) {
|
||||
tempIcon = Assets.waterLeakNormal;
|
||||
} else if (type == DeviceType.WaterLeak) {
|
||||
tempIcon = Assets.waterLeakNormal;
|
||||
} else {
|
||||
tempIcon = Assets.logoHorizontal;
|
||||
}
|
||||
@ -248,76 +254,51 @@ SOS
|
||||
switch (productType) {
|
||||
case 'AC':
|
||||
return [
|
||||
SwitchFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ModeFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
TempSetFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
CurrentTempFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
LevelFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ChildLockFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
SwitchFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ModeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
TempSetFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
CurrentTempFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
LevelFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ChildLockFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
];
|
||||
|
||||
case '1G':
|
||||
return [
|
||||
OneGangSwitchFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
OneGangCountdownFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
OneGangCountdownFunction(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
];
|
||||
|
||||
case '2G':
|
||||
return [
|
||||
TwoGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
TwoGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
TwoGangCountdown1Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
TwoGangCountdown2Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
TwoGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
TwoGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? ''),
|
||||
];
|
||||
|
||||
case '3G':
|
||||
return [
|
||||
ThreeGangSwitch1Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangSwitch2Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangSwitch3Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown1Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown2Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown3Function(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangSwitch1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangSwitch2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangSwitch3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown1Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown2Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
ThreeGangCountdown3Function(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
];
|
||||
case 'WPS':
|
||||
return [
|
||||
//IF Functions
|
||||
PresenceStateFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
CurrentDistanceFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
IlluminanceValueFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
PresenceTimeFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
PresenceStateFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
CurrentDistanceFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
IlluminanceValueFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
PresenceTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'IF'),
|
||||
|
||||
//THEN Functions
|
||||
FarDetectionFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
MotionSensitivityFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
MotionLessSensitivityFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
IndicatorFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
NoOneTimeFunction(
|
||||
deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
|
||||
FarDetectionFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
MotionSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
MotionLessSensitivityFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
IndicatorFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'BOTH'),
|
||||
NoOneTimeFunction(deviceId: uuid ?? '', deviceName: name ?? '', type: 'THEN'),
|
||||
];
|
||||
case 'GW':
|
||||
return [
|
||||
@ -337,6 +318,11 @@ SOS
|
||||
type: 'BOTH',
|
||||
),
|
||||
];
|
||||
case 'CPS':
|
||||
return CeilingSensorHelper.getCeilingSensorFunctions(
|
||||
uuid: uuid ?? '',
|
||||
name: name ?? '',
|
||||
);
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ class FactoryResetModel {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'devicesUuid': devicesUuid,
|
||||
'operationType': operationType,
|
||||
};
|
||||
}
|
||||
|
||||
@ -33,6 +34,7 @@ class FactoryResetModel {
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'devicesUuid': devicesUuid,
|
||||
'operationType': operationType,
|
||||
};
|
||||
}
|
||||
|
||||
@ -56,3 +58,4 @@ class FactoryResetModel {
|
||||
@override
|
||||
int get hashCode => devicesUuid.hashCode;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,8 @@ import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dar
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
class CeilingSensorControlsView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const CeilingSensorControlsView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
@ -31,29 +32,35 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
||||
..add(CeilingInitialEvent(device.uuid ?? '')),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
|
||||
if (state is CeilingLoadingInitialState ||
|
||||
state is CeilingReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is CeilingUpdateState) {
|
||||
return _buildGridView(
|
||||
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(context, state.ceilingSensorModel,
|
||||
isExtraLarge, isLarge, isMedium);
|
||||
} else if (state is CeilingReportsState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(BackToCeilingGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is ShowCeilingDescriptionState) {
|
||||
return DescriptionView(
|
||||
description: state.description,
|
||||
onClose: () {
|
||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(BackToCeilingGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is CeilingReportsFailedState) {
|
||||
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(
|
||||
context, model, isExtraLarge, isLarge, isMedium);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
@ -61,8 +68,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
|
||||
bool isLarge, bool isMedium) {
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
@ -143,8 +150,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CeilingSensorBloc>().add(
|
||||
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
|
||||
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||
code: 'presence_state', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.illuminanceRecordIcon,
|
||||
@ -153,9 +160,8 @@ class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLay
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
|
||||
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||
code: '', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.helpDescriptionIcon,
|
||||
|
@ -0,0 +1,237 @@
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:firebase_database/firebase_database.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||
import 'package:syncrow_web/services/control_device_service.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
part 'flush_mounted_presence_sensor_event.dart';
|
||||
part 'flush_mounted_presence_sensor_state.dart';
|
||||
|
||||
class FlushMountedPresenceSensorBloc
|
||||
extends Bloc<FlushMountedPresenceSensorEvent, FlushMountedPresenceSensorState> {
|
||||
final String deviceId;
|
||||
final ControlDeviceService controlDeviceService;
|
||||
final BatchControlDevicesService batchControlDevicesService;
|
||||
|
||||
late FlushMountedPresenceSensorModel deviceStatus;
|
||||
FlushMountedPresenceSensorBloc({
|
||||
required this.deviceId,
|
||||
required this.controlDeviceService,
|
||||
required this.batchControlDevicesService,
|
||||
}) : super(FlushMountedPresenceSensorInitialState()) {
|
||||
on<FlushMountedPresenceSensorFetchStatusEvent>(
|
||||
_onFlushMountedPresenceSensorFetchStatusEvent,
|
||||
);
|
||||
on<FlushMountedPresenceSensorFetchBatchStatusEvent>(
|
||||
_onFlushMountedPresenceSensorFetchBatchStatusEvent);
|
||||
on<FlushMountedPresenceSensorChangeValueEvent>(
|
||||
_onFlushMountedPresenceSensorChangeValueEvent,
|
||||
);
|
||||
on<FlushMountedPresenceSensorBatchControlEvent>(
|
||||
_onFlushMountedPresenceSensorBatchControlEvent,
|
||||
);
|
||||
on<FlushMountedPresenceSensorGetDeviceReportsEvent>(
|
||||
_onFlushMountedPresenceSensorGetDeviceReportsEvent);
|
||||
on<FlushMountedPresenceSensorShowDescriptionEvent>(
|
||||
_onFlushMountedPresenceSensorShowDescriptionEvent,
|
||||
);
|
||||
on<FlushMountedPresenceSensorBackToGridViewEvent>(
|
||||
_onFlushMountedPresenceSensorBackToGridViewEvent,
|
||||
);
|
||||
on<FlushMountedPresenceSensorFactoryResetEvent>(
|
||||
_onFlushMountedPresenceSensorFactoryResetEvent,
|
||||
);
|
||||
}
|
||||
|
||||
void _onFlushMountedPresenceSensorFetchStatusEvent(
|
||||
FlushMountedPresenceSensorFetchStatusEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorLoadingInitialState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
_listenToChanges(emit, deviceId);
|
||||
} catch (e) {
|
||||
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFlushMountedPresenceSensorFetchBatchStatusEvent(
|
||||
FlushMountedPresenceSensorFetchBatchStatusEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorLoadingInitialState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
deviceStatus = FlushMountedPresenceSensorModel.fromJson(response.status);
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
} catch (e) {
|
||||
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _listenToChanges(
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
String deviceId,
|
||||
) async {
|
||||
final ref = FirebaseDatabase.instance.ref(
|
||||
'device-status/$deviceId',
|
||||
);
|
||||
|
||||
await ref.onValue.listen(
|
||||
(DatabaseEvent event) async {
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<Status> statusList = [];
|
||||
|
||||
(usersMap['status'] as List<dynamic>?)?.forEach((element) {
|
||||
statusList.add(Status(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = FlushMountedPresenceSensorModel.fromJson(statusList);
|
||||
if (!emit.isDone) {
|
||||
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||
}
|
||||
},
|
||||
onError: (error) => log(error.toString(), name: 'FirebaseDatabaseError'),
|
||||
).asFuture();
|
||||
}
|
||||
|
||||
void _onFlushMountedPresenceSensorChangeValueEvent(
|
||||
FlushMountedPresenceSensorChangeValueEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||
_updateDeviceFunctionFromCode(event.code, event.value);
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
try {
|
||||
await controlDeviceService.controlDevice(
|
||||
deviceUuid: deviceId,
|
||||
status: Status(code: event.code, value: event.value),
|
||||
);
|
||||
} catch (_) {
|
||||
await _reloadDeviceStatus();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFlushMountedPresenceSensorBatchControlEvent(
|
||||
FlushMountedPresenceSensorBatchControlEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||
_updateDeviceFunctionFromCode(event.code, event.value);
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
|
||||
try {
|
||||
await batchControlDevicesService.batchControlDevices(
|
||||
uuids: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
);
|
||||
} catch (_) {
|
||||
await _reloadDeviceStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void _updateDeviceFunctionFromCode(String code, int value) {
|
||||
switch (code) {
|
||||
case FlushMountedPresenceSensorModel.codeFarDetection:
|
||||
deviceStatus.farDetection = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codeSensitivity:
|
||||
deviceStatus.sensitivity = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codeNoneDelay:
|
||||
deviceStatus.noneDelay = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codePresenceDelay:
|
||||
deviceStatus.presenceDelay = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codeNearDetection:
|
||||
deviceStatus.nearDetection = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codeOccurDistReduce:
|
||||
deviceStatus.occurDistReduce = value;
|
||||
break;
|
||||
case FlushMountedPresenceSensorModel.codeSensiReduce:
|
||||
deviceStatus.sensiReduce = value;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _reloadDeviceStatus() async {
|
||||
await Future.delayed(const Duration(milliseconds: 500), () {
|
||||
add(FlushMountedPresenceSensorFetchStatusEvent());
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onFlushMountedPresenceSensorGetDeviceReportsEvent(
|
||||
FlushMountedPresenceSensorGetDeviceReportsEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorDeviceReportsLoadingState());
|
||||
|
||||
try {
|
||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
||||
.then((value) {
|
||||
emit(FlushMountedPresenceSensorDeviceReportsState(
|
||||
deviceReport: value, code: event.code));
|
||||
});
|
||||
} catch (e) {
|
||||
emit(FlushMountedPresenceSensorDeviceReportsFailedState(error: e.toString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void _onFlushMountedPresenceSensorShowDescriptionEvent(
|
||||
FlushMountedPresenceSensorShowDescriptionEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) {
|
||||
emit(FlushMountedPresenceSensorShowDescriptionState(
|
||||
description: event.description));
|
||||
}
|
||||
|
||||
void _onFlushMountedPresenceSensorBackToGridViewEvent(
|
||||
FlushMountedPresenceSensorBackToGridViewEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) {
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
}
|
||||
|
||||
Future<void> _onFlushMountedPresenceSensorFactoryResetEvent(
|
||||
FlushMountedPresenceSensorFactoryResetEvent event,
|
||||
Emitter<FlushMountedPresenceSensorState> emit,
|
||||
) async {
|
||||
emit(FlushMountedPresenceSensorLoadingNewSate(model: deviceStatus));
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (!response) {
|
||||
emit(
|
||||
const FlushMountedPresenceSensorFailedState(
|
||||
error: 'Something went wrong with factory reset, please try again',
|
||||
),
|
||||
);
|
||||
} else {
|
||||
emit(FlushMountedPresenceSensorUpdateState(model: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(FlushMountedPresenceSensorFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
part of 'flush_mounted_presence_sensor_bloc.dart';
|
||||
|
||||
sealed class FlushMountedPresenceSensorEvent extends Equatable {
|
||||
const FlushMountedPresenceSensorEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorFetchStatusEvent
|
||||
extends FlushMountedPresenceSensorEvent {}
|
||||
|
||||
class FlushMountedPresenceSensorChangeValueEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final int value;
|
||||
final String code;
|
||||
final bool isBatchControl;
|
||||
const FlushMountedPresenceSensorChangeValueEvent({
|
||||
required this.value,
|
||||
required this.code,
|
||||
this.isBatchControl = false,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [value, code];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorFetchBatchStatusEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final List<String> devicesIds;
|
||||
const FlushMountedPresenceSensorFetchBatchStatusEvent(this.devicesIds);
|
||||
|
||||
@override
|
||||
List<Object> get props => [devicesIds];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorGetDeviceReportsEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final String deviceUuid;
|
||||
final String code;
|
||||
const FlushMountedPresenceSensorGetDeviceReportsEvent({
|
||||
required this.deviceUuid,
|
||||
required this.code,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceUuid, code];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorShowDescriptionEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final String description;
|
||||
const FlushMountedPresenceSensorShowDescriptionEvent({required this.description});
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorBackToGridViewEvent
|
||||
extends FlushMountedPresenceSensorEvent {}
|
||||
|
||||
class FlushMountedPresenceSensorBatchControlEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final dynamic value;
|
||||
|
||||
const FlushMountedPresenceSensorBatchControlEvent({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceIds, code, value];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorFactoryResetEvent
|
||||
extends FlushMountedPresenceSensorEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
const FlushMountedPresenceSensorFactoryResetEvent({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
part of 'flush_mounted_presence_sensor_bloc.dart';
|
||||
|
||||
sealed class FlushMountedPresenceSensorState extends Equatable {
|
||||
const FlushMountedPresenceSensorState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorInitialState
|
||||
extends FlushMountedPresenceSensorState {}
|
||||
|
||||
class FlushMountedPresenceSensorLoadingInitialState
|
||||
extends FlushMountedPresenceSensorState {}
|
||||
|
||||
class FlushMountedPresenceSensorUpdateState extends FlushMountedPresenceSensorState {
|
||||
final FlushMountedPresenceSensorModel model;
|
||||
const FlushMountedPresenceSensorUpdateState({required this.model});
|
||||
|
||||
@override
|
||||
List<Object> get props => [model];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorLoadingNewSate
|
||||
extends FlushMountedPresenceSensorState {
|
||||
final FlushMountedPresenceSensorModel model;
|
||||
const FlushMountedPresenceSensorLoadingNewSate({required this.model});
|
||||
|
||||
@override
|
||||
List<Object> get props => [model];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorFailedState extends FlushMountedPresenceSensorState {
|
||||
final String error;
|
||||
|
||||
const FlushMountedPresenceSensorFailedState({required this.error});
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorDeviceReportsLoadingState
|
||||
extends FlushMountedPresenceSensorState {}
|
||||
|
||||
class FlushMountedPresenceSensorDeviceReportsState
|
||||
extends FlushMountedPresenceSensorState {
|
||||
const FlushMountedPresenceSensorDeviceReportsState({
|
||||
required this.deviceReport,
|
||||
required this.code,
|
||||
});
|
||||
|
||||
final DeviceReport deviceReport;
|
||||
final String code;
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceReport, code];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorDeviceReportsFailedState
|
||||
extends FlushMountedPresenceSensorState {
|
||||
const FlushMountedPresenceSensorDeviceReportsFailedState({required this.error});
|
||||
|
||||
final String error;
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class FlushMountedPresenceSensorShowDescriptionState
|
||||
extends FlushMountedPresenceSensorState {
|
||||
const FlushMountedPresenceSensorShowDescriptionState({required this.description});
|
||||
|
||||
final String description;
|
||||
@override
|
||||
List<Object> get props => [description];
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||
import 'package:syncrow_web/services/batch_control_devices_service.dart';
|
||||
import 'package:syncrow_web/services/control_device_service.dart';
|
||||
|
||||
abstract final class FlushMountedPresenceSensorBlocFactory {
|
||||
const FlushMountedPresenceSensorBlocFactory._();
|
||||
|
||||
static FlushMountedPresenceSensorBloc create({
|
||||
required String deviceId,
|
||||
}) {
|
||||
return FlushMountedPresenceSensorBloc(
|
||||
deviceId: deviceId,
|
||||
controlDeviceService: DebouncedControlDeviceService(
|
||||
decoratee: RemoteControlDeviceService(),
|
||||
),
|
||||
batchControlDevicesService: DebouncedBatchControlDevicesService(
|
||||
decoratee: RemoteBatchControlDevicesService(),
|
||||
),
|
||||
)..add(FlushMountedPresenceSensorFetchStatusEvent());
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class FlushMountedPresenceSensorModel {
|
||||
FlushMountedPresenceSensorModel({
|
||||
required this.presenceState,
|
||||
required this.farDetection,
|
||||
required this.illuminance,
|
||||
required this.sensitivity,
|
||||
required this.occurDistReduce,
|
||||
required this.noneDelay,
|
||||
required this.presenceDelay,
|
||||
required this.nearDetection,
|
||||
required this.sensiReduce,
|
||||
required this.checkingResult,
|
||||
});
|
||||
|
||||
static const String codePresenceState = 'presence_state';
|
||||
static const String codeSensitivity = 'sensitivity';
|
||||
static const String codeNearDetection = 'near_detection';
|
||||
static const String codeFarDetection = 'far_detection';
|
||||
static const String codeCheckingResult = 'checking_result';
|
||||
static const String codePresenceDelay = 'presence_delay';
|
||||
static const String codeNoneDelay = 'none_delay';
|
||||
static const String codeOccurDistReduce = 'occur_dist_reduce';
|
||||
static const String codeIlluminance = 'illum_value';
|
||||
static const String codeSensiReduce = 'sensi_reduce';
|
||||
|
||||
String presenceState;
|
||||
int sensitivity;
|
||||
int nearDetection;
|
||||
int farDetection;
|
||||
String checkingResult;
|
||||
int presenceDelay;
|
||||
int noneDelay;
|
||||
int occurDistReduce;
|
||||
int illuminance;
|
||||
int sensiReduce;
|
||||
|
||||
factory FlushMountedPresenceSensorModel.fromJson(List<Status> jsonList) {
|
||||
String presenceState = 'none';
|
||||
int sensitivity = 0;
|
||||
int nearDetection = 0;
|
||||
int farDetection = 0;
|
||||
String checkingResult = 'none';
|
||||
int presenceDelay = 0;
|
||||
int noneDelay = 0;
|
||||
int occurDistReduce = 0;
|
||||
int illuminance = 0;
|
||||
int sensiReduce = 0;
|
||||
|
||||
for (var status in jsonList) {
|
||||
switch (status.code) {
|
||||
case codePresenceState:
|
||||
presenceState = status.value ?? 'presence';
|
||||
break;
|
||||
case codeSensitivity:
|
||||
sensitivity = status.value ?? 0;
|
||||
break;
|
||||
case codeNearDetection:
|
||||
nearDetection = status.value ?? 0;
|
||||
break;
|
||||
case codeFarDetection:
|
||||
farDetection = status.value ?? 0;
|
||||
break;
|
||||
case codeCheckingResult:
|
||||
checkingResult = status.value ?? 'check_success';
|
||||
break;
|
||||
case codePresenceDelay:
|
||||
presenceDelay = status.value ?? 0;
|
||||
break;
|
||||
case codeNoneDelay:
|
||||
noneDelay = status.value ?? 0;
|
||||
break;
|
||||
case codeOccurDistReduce:
|
||||
occurDistReduce = status.value ?? 0;
|
||||
break;
|
||||
case codeIlluminance:
|
||||
illuminance = status.value ?? 0;
|
||||
break;
|
||||
case codeSensiReduce:
|
||||
sensiReduce = status.value ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return FlushMountedPresenceSensorModel(
|
||||
presenceState: presenceState,
|
||||
sensitivity: sensitivity,
|
||||
nearDetection: nearDetection,
|
||||
farDetection: farDetection,
|
||||
checkingResult: checkingResult,
|
||||
presenceDelay: presenceDelay,
|
||||
noneDelay: noneDelay,
|
||||
occurDistReduce: occurDistReduce,
|
||||
illuminance: illuminance,
|
||||
sensiReduce: sensiReduce,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class FlushMountedPresenceSensorBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const FlushMountedPresenceSensorBatchControlView({
|
||||
required this.devicesIds,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<String> devicesIds;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||
deviceId: devicesIds.first,
|
||||
),
|
||||
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||
FlushMountedPresenceSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is FlushMountedPresenceSensorLoadingInitialState ||
|
||||
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is FlushMountedPresenceSensorUpdateState) {
|
||||
return _buildGridView(context, state.model);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(
|
||||
BuildContext context,
|
||||
FlushMountedPresenceSensorModel model,
|
||||
) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isLarge || isExtraLarge
|
||||
? 3
|
||||
: isMedium
|
||||
? 2
|
||||
: 1,
|
||||
mainAxisExtent: 140,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
children: [
|
||||
PresenceUpdateData(
|
||||
value: model.sensitivity.toDouble(),
|
||||
title: 'Sensitivity:',
|
||||
minValue: 0,
|
||||
maxValue: 9,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.nearDetection / 100).toDouble(),
|
||||
title: 'Nearest Detect Dist:',
|
||||
description: 'm',
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
steps: 0.1,
|
||||
valuesPercision: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
||||
value: (value * 100).toInt(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.farDetection / 100).toDouble(),
|
||||
title: 'Max Detect Dist:',
|
||||
description: 'm',
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
steps: 0.1,
|
||||
valuesPercision: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
||||
value: (value * 100).toInt(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: model.presenceDelay.toDouble(),
|
||||
title: 'Trigger Level:',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.occurDistReduce.toDouble()),
|
||||
title: 'Indent Level:',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.sensiReduce.toDouble()),
|
||||
title: 'Target Confirm Time:',
|
||||
description: 's',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: ((model.noneDelay / 10).toDouble()),
|
||||
description: 's',
|
||||
title: 'Disappe Delay:',
|
||||
minValue: 20,
|
||||
maxValue: 300,
|
||||
steps: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||
value: (value * 10).round(),
|
||||
),
|
||||
),
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorFactoryResetEvent(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/bloc/flush_mounted_presence_sensor_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/factories/flush_mounted_presence_sensor_bloc_factory.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/flush_mounted_presence_sensor/models/flush_mounted_presence_sensor_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/table/description_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class FlushMountedPresenceSensorControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const FlushMountedPresenceSensorControlView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => FlushMountedPresenceSensorBlocFactory.create(
|
||||
deviceId: device.uuid ?? '-1',
|
||||
),
|
||||
child: BlocBuilder<FlushMountedPresenceSensorBloc,
|
||||
FlushMountedPresenceSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is FlushMountedPresenceSensorLoadingInitialState ||
|
||||
state is FlushMountedPresenceSensorDeviceReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is FlushMountedPresenceSensorUpdateState) {
|
||||
return _buildGridView(context, state.model);
|
||||
} else if (state is FlushMountedPresenceSensorDeviceReportsState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
thirdColumnTitle:
|
||||
state.code == 'illuminance_value' ? "Value" : 'Status',
|
||||
thirdColumnDescription:
|
||||
state.code == 'illuminance_value' ? "Lux" : null,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context
|
||||
.read<FlushMountedPresenceSensorBloc>()
|
||||
.add(FlushMountedPresenceSensorBackToGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is FlushMountedPresenceSensorShowDescriptionState) {
|
||||
return DescriptionView(
|
||||
description: state.description,
|
||||
onClose: () {
|
||||
context
|
||||
.read<FlushMountedPresenceSensorBloc>()
|
||||
.add(FlushMountedPresenceSensorBackToGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is FlushMountedPresenceSensorDeviceReportsFailedState) {
|
||||
final model =
|
||||
context.read<FlushMountedPresenceSensorBloc>().deviceStatus;
|
||||
return _buildGridView(context, model);
|
||||
}
|
||||
return const Center(
|
||||
child: Text('Error fetching status', textAlign: TextAlign.center),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(
|
||||
BuildContext context,
|
||||
FlushMountedPresenceSensorModel model,
|
||||
) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isLarge || isExtraLarge
|
||||
? 3
|
||||
: isMedium
|
||||
? 2
|
||||
: 1,
|
||||
mainAxisExtent: 140,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
children: [
|
||||
PresenceState(
|
||||
value: model.presenceState,
|
||||
),
|
||||
PresenceDisplayValue(
|
||||
value: model.illuminance.toString(),
|
||||
postfix: 'Lux',
|
||||
description: 'Illuminance Value',
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: model.sensitivity.toDouble(),
|
||||
title: 'Sensitivity:',
|
||||
minValue: 0,
|
||||
maxValue: 9,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeSensitivity,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.nearDetection / 100).toDouble(),
|
||||
title: 'Nearest Detect Dist:',
|
||||
description: 'm',
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
steps: 0.1,
|
||||
valuesPercision: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeNearDetection,
|
||||
value: (value * 100).toInt(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.farDetection / 100).toDouble(),
|
||||
title: 'Max Detect Dist:',
|
||||
description: 'm',
|
||||
minValue: 0.0,
|
||||
maxValue: 9.5,
|
||||
steps: 0.1,
|
||||
valuesPercision: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeFarDetection,
|
||||
value: (value * 100).toInt(),
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.presenceDelay.toDouble()),
|
||||
title: 'Trigger Level:',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codePresenceDelay,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.occurDistReduce.toDouble()),
|
||||
title: 'Indent Level:',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeOccurDistReduce,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: (model.sensiReduce.toDouble()),
|
||||
title: 'Target Confirm Time:',
|
||||
description: 's',
|
||||
minValue: 0,
|
||||
maxValue: 3,
|
||||
steps: 1,
|
||||
action: (int value) => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeSensiReduce,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
),
|
||||
PresenceUpdateData(
|
||||
value: ((model.noneDelay / 10).toDouble()),
|
||||
description: 's',
|
||||
title: 'Disappe Delay:',
|
||||
minValue: 20,
|
||||
maxValue: 300,
|
||||
steps: 1,
|
||||
action: (double value) =>
|
||||
context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorChangeValueEvent(
|
||||
code: FlushMountedPresenceSensorModel.codeNoneDelay,
|
||||
value: (value * 10).round(),
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () => context.read<FlushMountedPresenceSensorBloc>().add(
|
||||
FlushMountedPresenceSensorGetDeviceReportsEvent(
|
||||
code: 'presence_state',
|
||||
deviceUuid: device.uuid!,
|
||||
),
|
||||
),
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.presenceRecordIcon,
|
||||
description: 'Presence Record',
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
|
||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/batch_control/factory_reset.dart';
|
||||
// import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||
|
||||
class MainDoorSensorBatchView extends StatelessWidget {
|
||||
const MainDoorSensorBatchView({super.key, required this.devicesIds});
|
||||
@ -13,35 +12,31 @@ class MainDoorSensorBatchView extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// SizedBox(
|
||||
// width: 170,
|
||||
// height: 140,
|
||||
// child: FirmwareUpdateWidget(
|
||||
// deviceId: devicesIds.first,
|
||||
// version: 12,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// width: 12,
|
||||
// ),
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 140,
|
||||
child: FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
BlocProvider.of<MainDoorSensorBloc>(context).add(
|
||||
MainDoorSensorFactoryReset(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
return BlocProvider(
|
||||
create: (context) => MainDoorSensorBloc(),
|
||||
child: Builder(
|
||||
builder: (innerContext) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 140,
|
||||
child: FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
BlocProvider.of<MainDoorSensorBloc>(innerContext).add(
|
||||
MainDoorSensorFactoryReset(
|
||||
deviceId: devicesIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class PresenceUpdateData extends StatefulWidget {
|
||||
const PresenceUpdateData({
|
||||
@ -13,6 +14,7 @@ class PresenceUpdateData extends StatefulWidget {
|
||||
required this.maxValue,
|
||||
required this.steps,
|
||||
this.description,
|
||||
this.valuesPercision = 0,
|
||||
});
|
||||
|
||||
final String title;
|
||||
@ -22,6 +24,7 @@ class PresenceUpdateData extends StatefulWidget {
|
||||
final double steps;
|
||||
final Function action;
|
||||
final String? description;
|
||||
final int valuesPercision;
|
||||
|
||||
@override
|
||||
State<PresenceUpdateData> createState() => _CurrentTempState();
|
||||
@ -45,7 +48,7 @@ class _CurrentTempState extends State<PresenceUpdateData> {
|
||||
}
|
||||
|
||||
void _onValueChanged(double newValue) {
|
||||
widget.action(newValue.toInt());
|
||||
widget.action(newValue);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -62,11 +65,14 @@ class _CurrentTempState extends State<PresenceUpdateData> {
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.blackColor, fontWeight: FontWeight.w400, fontSize: 10),
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10,
|
||||
),
|
||||
),
|
||||
IncrementDecrementWidget(
|
||||
value: widget.value.toString(),
|
||||
value: widget.value.toStringAsFixed(widget.valuesPercision),
|
||||
description: widget.description ?? '',
|
||||
descriptionColor: ColorsManager.blackColor,
|
||||
onIncrement: () {
|
||||
|
@ -84,6 +84,16 @@ class _PresenceUpdateDataState extends State<PresenceNoBodyTime> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PresenceNoBodyTime oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.value != widget.value) {
|
||||
setState(() {
|
||||
_currentValue = widget.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DeviceControlsContainer(
|
||||
|
@ -62,9 +62,6 @@ class ToggleWidget extends StatelessWidget {
|
||||
)),
|
||||
if (showToggle)
|
||||
Container(
|
||||
height: 20,
|
||||
width: 35,
|
||||
padding: const EdgeInsets.only(right: 16, top: 10),
|
||||
child: CupertinoSwitch(
|
||||
value: value,
|
||||
activeColor: ColorsManager.dialogBlueTitle,
|
||||
|
@ -13,7 +13,8 @@ import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_la
|
||||
|
||||
import '../models/sos_status_model.dart';
|
||||
|
||||
class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
class SosDeviceControlsView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
const SosDeviceControlsView({
|
||||
super.key,
|
||||
required this.device,
|
||||
@ -24,7 +25,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
|
||||
create: (context) =>
|
||||
SosDeviceBloc()..add(GetDeviceStatus(device.uuid!)),
|
||||
child: BlocBuilder<SosDeviceBloc, SosDeviceState>(
|
||||
builder: (context, state) {
|
||||
if (state is SosDeviceLoadingState) {
|
||||
@ -63,7 +65,8 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, SosStatusModel deviceStatus) {
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, SosStatusModel deviceStatus) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
@ -85,7 +88,7 @@ class SosDeviceControlsView extends StatelessWidget with HelperResponsiveLayout
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: deviceStatus.sosStatus == 'sos' ? 'SOS' : 'Normal',
|
||||
icon: deviceStatus.sosStatus == 'sos' ? Assets.sos : Assets.sosNormal,
|
||||
icon: deviceStatus.sosStatus == 'sos' ? Assets.sosNormal : Assets.sos,
|
||||
onTap: () {},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
|
@ -21,6 +21,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
on<ShowDescriptionEvent>(_showDescription);
|
||||
on<BackToGridViewEvent>(_backToGridView);
|
||||
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
||||
on<WallSensorRealtimeUpdateEvent>(_onRealtimeUpdate);
|
||||
}
|
||||
|
||||
void _fetchWallSensorStatus(
|
||||
@ -30,7 +31,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||
deviceStatus = WallSensorModel.fromJson(response.status);
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
_listenToChanges(emit, deviceId);
|
||||
_listenToChanges(deviceId);
|
||||
} catch (e) {
|
||||
emit(WallSensorFailedState(error: e.toString()));
|
||||
return;
|
||||
@ -52,28 +53,27 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
_listenToChanges(Emitter<WallSensorState> emit, deviceId) {
|
||||
try {
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||
Stream<DatabaseEvent> stream = ref.onValue;
|
||||
void _listenToChanges(String deviceId) {
|
||||
DatabaseReference ref =
|
||||
FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||
ref.onValue.listen((DatabaseEvent event) {
|
||||
final data = event.snapshot.value as Map<dynamic, dynamic>?;
|
||||
if (data == null) return;
|
||||
|
||||
stream.listen((DatabaseEvent event) {
|
||||
Map<dynamic, dynamic> usersMap =
|
||||
event.snapshot.value as Map<dynamic, dynamic>;
|
||||
List<Status> statusList = [];
|
||||
final statusList = (data['status'] as List?)
|
||||
?.map((e) => Status(code: e['code'], value: e['value']))
|
||||
.toList();
|
||||
|
||||
usersMap['status'].forEach((element) {
|
||||
statusList
|
||||
.add(Status(code: element['code'], value: element['value']));
|
||||
});
|
||||
|
||||
deviceStatus = WallSensorModel.fromJson(statusList);
|
||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||
});
|
||||
} catch (_) {}
|
||||
if (statusList != null) {
|
||||
final updatedDeviceStatus = WallSensorModel.fromJson(statusList);
|
||||
if (!isClosed) {
|
||||
add(WallSensorRealtimeUpdateEvent(updatedDeviceStatus));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void _changeValue(
|
||||
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||
@ -195,4 +195,12 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
emit(WallSensorFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _onRealtimeUpdate(
|
||||
WallSensorRealtimeUpdateEvent event,
|
||||
Emitter<WallSensorState> emit,
|
||||
) {
|
||||
deviceStatus = event.deviceStatus;
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
|
||||
|
||||
abstract class WallSensorEvent extends Equatable {
|
||||
const WallSensorEvent();
|
||||
@ -70,3 +71,8 @@ class WallSensorFactoryResetEvent extends WallSensorEvent {
|
||||
required this.factoryReset,
|
||||
});
|
||||
}
|
||||
|
||||
class WallSensorRealtimeUpdateEvent extends WallSensorEvent {
|
||||
final WallSensorModel deviceStatus;
|
||||
const WallSensorRealtimeUpdateEvent(this.deviceStatus);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ class FunctionBloc extends Bloc<FunctionBlocEvent, FunctionBlocState> {
|
||||
condition: event.functionData.condition ?? existingData.condition,
|
||||
);
|
||||
} else {
|
||||
functions.clear();
|
||||
functions.add(event.functionData);
|
||||
}
|
||||
|
||||
|
@ -64,8 +64,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
TriggerSwitchTabsEvent event,
|
||||
Emitter<RoutineState> emit,
|
||||
) {
|
||||
emit(state.copyWith(
|
||||
routineTab: event.isRoutineTab, createRoutineView: false));
|
||||
emit(state.copyWith(routineTab: event.isRoutineTab, createRoutineView: false));
|
||||
add(ResetRoutineState());
|
||||
if (event.isRoutineTab) {
|
||||
add(const LoadScenes());
|
||||
@ -91,8 +90,8 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
final updatedIfItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||
|
||||
// Find the index of the item in teh current itemsList
|
||||
int index = updatedIfItems.indexWhere(
|
||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||
int index =
|
||||
updatedIfItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||
// Replace the map if the index is valid
|
||||
if (index != -1) {
|
||||
updatedIfItems[index] = event.item;
|
||||
@ -101,21 +100,18 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
|
||||
if (event.isTabToRun) {
|
||||
emit(state.copyWith(
|
||||
ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
||||
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: true, isAutomation: false));
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
||||
emit(state.copyWith(ifItems: updatedIfItems, isTabToRun: false, isAutomation: true));
|
||||
}
|
||||
}
|
||||
|
||||
void _onAddToThenContainer(
|
||||
AddToThenContainer event, Emitter<RoutineState> emit) {
|
||||
void _onAddToThenContainer(AddToThenContainer event, Emitter<RoutineState> emit) {
|
||||
final currentItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||
|
||||
// Find the index of the item in teh current itemsList
|
||||
int index = currentItems.indexWhere(
|
||||
(map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||
int index =
|
||||
currentItems.indexWhere((map) => map['uniqueCustomId'] == event.item['uniqueCustomId']);
|
||||
// Replace the map if the index is valid
|
||||
if (index != -1) {
|
||||
currentItems[index] = event.item;
|
||||
@ -126,45 +122,42 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
emit(state.copyWith(thenItems: currentItems));
|
||||
}
|
||||
|
||||
void _onAddFunctionsToRoutine(
|
||||
AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
||||
void _onAddFunctionsToRoutine(AddFunctionToRoutine event, Emitter<RoutineState> emit) {
|
||||
try {
|
||||
if (event.functions.isEmpty) return;
|
||||
|
||||
List<DeviceFunctionData> selectedFunction =
|
||||
List<DeviceFunctionData>.from(event.functions);
|
||||
// List<DeviceFunctionData> selectedFunction = List<DeviceFunctionData>.from(event.functions);
|
||||
|
||||
Map<String, List<DeviceFunctionData>> currentSelectedFunctions =
|
||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||
if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
|
||||
List<DeviceFunctionData> currentFunctions =
|
||||
List<DeviceFunctionData>.from(
|
||||
currentSelectedFunctions[event.uniqueCustomId] ?? []);
|
||||
|
||||
List<String> functionCode = [];
|
||||
for (int i = 0; i < selectedFunction.length; i++) {
|
||||
for (int j = 0; j < currentFunctions.length; j++) {
|
||||
if (selectedFunction[i].functionCode ==
|
||||
currentFunctions[j].functionCode) {
|
||||
currentFunctions[j] = selectedFunction[i];
|
||||
if (!functionCode.contains(currentFunctions[j].functionCode)) {
|
||||
functionCode.add(currentFunctions[j].functionCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (currentSelectedFunctions.containsKey(event.uniqueCustomId)) {
|
||||
// List<DeviceFunctionData> currentFunctions =
|
||||
// List<DeviceFunctionData>.from(currentSelectedFunctions[event.uniqueCustomId] ?? []);
|
||||
|
||||
for (int i = 0; i < functionCode.length; i++) {
|
||||
selectedFunction
|
||||
.removeWhere((code) => code.functionCode == functionCode[i]);
|
||||
}
|
||||
// List<String> functionCode = [];
|
||||
// for (int i = 0; i < selectedFunction.length; i++) {
|
||||
// for (int j = 0; j < currentFunctions.length; j++) {
|
||||
// if (selectedFunction[i].functionCode == currentFunctions[j].functionCode) {
|
||||
// currentFunctions[j] = selectedFunction[i];
|
||||
// if (!functionCode.contains(currentFunctions[j].functionCode)) {
|
||||
// functionCode.add(currentFunctions[j].functionCode);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
currentSelectedFunctions[event.uniqueCustomId] =
|
||||
List.from(currentFunctions)..addAll(selectedFunction);
|
||||
} else {
|
||||
currentSelectedFunctions[event.uniqueCustomId] =
|
||||
List.from(event.functions);
|
||||
}
|
||||
// for (int i = 0; i < functionCode.length; i++) {
|
||||
// selectedFunction.removeWhere((code) => code.functionCode == functionCode[i]);
|
||||
// }
|
||||
|
||||
// currentSelectedFunctions[event.uniqueCustomId] = List.from(currentFunctions)
|
||||
// ..addAll(selectedFunction);
|
||||
// } else {
|
||||
// currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||
// }
|
||||
|
||||
currentSelectedFunctions[event.uniqueCustomId] = List.from(event.functions);
|
||||
|
||||
emit(state.copyWith(selectedFunctions: currentSelectedFunctions));
|
||||
} catch (e) {
|
||||
@ -172,30 +165,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadScenes(
|
||||
LoadScenes event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onLoadScenes(LoadScenes event, Emitter<RoutineState> emit) async {
|
||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||
List<ScenesModel> scenes = [];
|
||||
try {
|
||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||
createRoutineBloc.selectedCommunityId == '') {
|
||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
for (var spaceId in spacesList) {
|
||||
scenes.addAll(
|
||||
await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||
scenes.addAll(await SceneApi.getScenes(spaceId, communityId, projectUuid));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scenes.addAll(await SceneApi.getScenes(
|
||||
createRoutineBloc.selectedSpaceId,
|
||||
createRoutineBloc.selectedCommunityId,
|
||||
projectUuid));
|
||||
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectUuid));
|
||||
}
|
||||
|
||||
emit(state.copyWith(
|
||||
@ -212,8 +199,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onLoadAutomation(
|
||||
LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onLoadAutomation(LoadAutomation event, Emitter<RoutineState> emit) async {
|
||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||
List<ScenesModel> automations = [];
|
||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||
@ -221,23 +207,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||
try {
|
||||
|
||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||
createRoutineBloc.selectedCommunityId == '') {
|
||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
for (var spaceId in spacesList) {
|
||||
automations.addAll(
|
||||
await SceneApi.getAutomation(spaceId, communityId, projectId));
|
||||
automations.addAll(await SceneApi.getAutomation(spaceId, communityId, projectId));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
automations.addAll(await SceneApi.getAutomation(
|
||||
createRoutineBloc.selectedSpaceId,
|
||||
createRoutineBloc.selectedCommunityId,
|
||||
projectId));
|
||||
createRoutineBloc.selectedSpaceId, createRoutineBloc.selectedCommunityId, projectId));
|
||||
}
|
||||
emit(state.copyWith(
|
||||
automations: automations,
|
||||
@ -253,16 +233,14 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onSearchRoutines(
|
||||
SearchRoutines event, Emitter<RoutineState> emit) async {
|
||||
FutureOr<void> _onSearchRoutines(SearchRoutines event, Emitter<RoutineState> emit) async {
|
||||
emit(state.copyWith(isLoading: true, errorMessage: null));
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
emit(state.copyWith(isLoading: false, errorMessage: null));
|
||||
emit(state.copyWith(searchText: event.query));
|
||||
}
|
||||
|
||||
FutureOr<void> _onAddSelectedIcon(
|
||||
AddSelectedIcon event, Emitter<RoutineState> emit) {
|
||||
FutureOr<void> _onAddSelectedIcon(AddSelectedIcon event, Emitter<RoutineState> emit) {
|
||||
emit(state.copyWith(selectedIcon: event.icon));
|
||||
}
|
||||
|
||||
@ -276,8 +254,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
return actions.last['deviceId'] == 'delay';
|
||||
}
|
||||
|
||||
Future<void> _onCreateScene(
|
||||
CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onCreateScene(CreateSceneEvent event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
// Check if first action is delay
|
||||
// if (_isFirstActionDelay(state.thenItems)) {
|
||||
@ -290,8 +267,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
|
||||
if (_isLastActionDelay(state.thenItems)) {
|
||||
emit(state.copyWith(
|
||||
errorMessage:
|
||||
'A delay condition cannot be the only or the last action',
|
||||
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||
isLoading: false,
|
||||
));
|
||||
return;
|
||||
@ -367,8 +343,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onCreateAutomation(
|
||||
CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onCreateAutomation(CreateAutomationEvent event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||
@ -390,8 +365,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
|
||||
if (_isLastActionDelay(state.thenItems)) {
|
||||
emit(state.copyWith(
|
||||
errorMessage:
|
||||
'A delay condition cannot be the only or the last action',
|
||||
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||
isLoading: false,
|
||||
));
|
||||
CustomSnackBar.redSnackBar('Cannot have delay as the last action');
|
||||
@ -482,8 +456,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
actions: actions,
|
||||
);
|
||||
|
||||
final result =
|
||||
await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
||||
final result = await SceneApi.createAutomation(createAutomationModel, projectUuid);
|
||||
if (result['success']) {
|
||||
add(ResetRoutineState());
|
||||
add(const LoadAutomation());
|
||||
@ -504,21 +477,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onRemoveDragCard(
|
||||
RemoveDragCard event, Emitter<RoutineState> emit) {
|
||||
FutureOr<void> _onRemoveDragCard(RemoveDragCard event, Emitter<RoutineState> emit) {
|
||||
if (event.isFromThen) {
|
||||
final thenItems = List<Map<String, dynamic>>.from(state.thenItems);
|
||||
final selectedFunctions =
|
||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||
|
||||
thenItems.removeAt(event.index);
|
||||
selectedFunctions.remove(event.key);
|
||||
emit(state.copyWith(
|
||||
thenItems: thenItems, selectedFunctions: selectedFunctions));
|
||||
emit(state.copyWith(thenItems: thenItems, selectedFunctions: selectedFunctions));
|
||||
} else {
|
||||
final ifItems = List<Map<String, dynamic>>.from(state.ifItems);
|
||||
final selectedFunctions =
|
||||
Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||
final selectedFunctions = Map<String, List<DeviceFunctionData>>.from(state.selectedFunctions);
|
||||
|
||||
ifItems.removeAt(event.index);
|
||||
selectedFunctions.remove(event.key);
|
||||
@ -529,8 +498,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
isAutomation: false,
|
||||
isTabToRun: false));
|
||||
} else {
|
||||
emit(state.copyWith(
|
||||
ifItems: ifItems, selectedFunctions: selectedFunctions));
|
||||
emit(state.copyWith(ifItems: ifItems, selectedFunctions: selectedFunctions));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -542,141 +510,138 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
));
|
||||
}
|
||||
|
||||
FutureOr<void> _onEffectiveTimeEvent(
|
||||
EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
||||
FutureOr<void> _onEffectiveTimeEvent(EffectiveTimePeriodEvent event, Emitter<RoutineState> emit) {
|
||||
emit(state.copyWith(effectiveTime: event.effectiveTime));
|
||||
}
|
||||
|
||||
FutureOr<void> _onSetRoutineName(
|
||||
SetRoutineName event, Emitter<RoutineState> emit) {
|
||||
FutureOr<void> _onSetRoutineName(SetRoutineName event, Emitter<RoutineState> emit) {
|
||||
emit(state.copyWith(
|
||||
routineName: event.name,
|
||||
));
|
||||
}
|
||||
|
||||
(
|
||||
List<Map<String, dynamic>>,
|
||||
List<Map<String, dynamic>>,
|
||||
Map<String, List<DeviceFunctionData>>
|
||||
) _createCardData(
|
||||
List<RoutineAction> actions,
|
||||
List<RoutineCondition>? conditions,
|
||||
Map<String, List<DeviceFunctionData>> currentFunctions,
|
||||
bool isTabToRun,
|
||||
) {
|
||||
final ifItems = isTabToRun
|
||||
? [
|
||||
{
|
||||
'entityId': 'tab_to_run',
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'deviceId': 'tab_to_run',
|
||||
'title': 'Tab to run',
|
||||
'productType': 'tab_to_run',
|
||||
'imagePath': Assets.tabToRun,
|
||||
}
|
||||
]
|
||||
: conditions?.map((condition) {
|
||||
final matchingDevice = state.devices.firstWhere(
|
||||
(device) => device.uuid == condition.entityId,
|
||||
orElse: () => AllDevicesModel(
|
||||
uuid: condition.entityId,
|
||||
name: condition.entityId,
|
||||
productType: condition.entityType,
|
||||
),
|
||||
);
|
||||
// (
|
||||
// List<Map<String, dynamic>>,
|
||||
// List<Map<String, dynamic>>,
|
||||
// Map<String, List<DeviceFunctionData>>
|
||||
// ) _createCardData(
|
||||
// List<RoutineAction> actions,
|
||||
// List<RoutineCondition>? conditions,
|
||||
// Map<String, List<DeviceFunctionData>> currentFunctions,
|
||||
// bool isTabToRun,
|
||||
// ) {
|
||||
// final ifItems = isTabToRun
|
||||
// ? [
|
||||
// {
|
||||
// 'entityId': 'tab_to_run',
|
||||
// 'uniqueCustomId': const Uuid().v4(),
|
||||
// 'deviceId': 'tab_to_run',
|
||||
// 'title': 'Tab to run',
|
||||
// 'productType': 'tab_to_run',
|
||||
// 'imagePath': Assets.tabToRun,
|
||||
// }
|
||||
// ]
|
||||
// : conditions?.map((condition) {
|
||||
// final matchingDevice = state.devices.firstWhere(
|
||||
// (device) => device.uuid == condition.entityId,
|
||||
// orElse: () => AllDevicesModel(
|
||||
// uuid: condition.entityId,
|
||||
// name: condition.entityId,
|
||||
// productType: condition.entityType,
|
||||
// ),
|
||||
// );
|
||||
|
||||
final cardData = {
|
||||
'entityId': condition.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'deviceId': condition.entityId,
|
||||
'title': matchingDevice.name ?? condition.entityId,
|
||||
'productType': condition.entityType,
|
||||
'imagePath':
|
||||
matchingDevice.getDefaultIcon(condition.entityType),
|
||||
};
|
||||
// final cardData = {
|
||||
// 'entityId': condition.entityId,
|
||||
// 'uniqueCustomId': const Uuid().v4(),
|
||||
// 'deviceId': condition.entityId,
|
||||
// 'title': matchingDevice.name ?? condition.entityId,
|
||||
// 'productType': condition.entityType,
|
||||
// 'imagePath':
|
||||
// matchingDevice.getDefaultIcon(condition.entityType),
|
||||
// };
|
||||
|
||||
final functions = matchingDevice.functions;
|
||||
// final functions = matchingDevice.functions;
|
||||
|
||||
for (var function in functions) {
|
||||
if (function.code == condition.expr.statusCode) {
|
||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
DeviceFunctionData(
|
||||
entityId: condition.entityId,
|
||||
functionCode: condition.expr.statusCode,
|
||||
value: condition.expr.statusValue,
|
||||
operationName: function.operationName,
|
||||
),
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
// for (var function in functions) {
|
||||
// if (function.code == condition.expr.statusCode) {
|
||||
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
// DeviceFunctionData(
|
||||
// entityId: condition.entityId,
|
||||
// functionCode: condition.expr.statusCode,
|
||||
// value: condition.expr.statusValue,
|
||||
// operationName: function.operationName,
|
||||
// ),
|
||||
// ];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
return cardData;
|
||||
}).toList() ??
|
||||
[];
|
||||
// return cardData;
|
||||
// }).toList() ??
|
||||
// [];
|
||||
|
||||
final thenItems = actions.map((action) {
|
||||
final matchingDevice = state.devices.firstWhere(
|
||||
(device) => device.uuid == action.entityId,
|
||||
orElse: () => AllDevicesModel(
|
||||
uuid: action.entityId,
|
||||
name: action.entityId,
|
||||
productType: action.productType,
|
||||
),
|
||||
);
|
||||
// final thenItems = actions.map((action) {
|
||||
// final matchingDevice = state.devices.firstWhere(
|
||||
// (device) => device.uuid == action.entityId,
|
||||
// orElse: () => AllDevicesModel(
|
||||
// uuid: action.entityId,
|
||||
// name: action.entityId,
|
||||
// productType: action.productType,
|
||||
// ),
|
||||
// );
|
||||
|
||||
final cardData = {
|
||||
'entityId': action.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'deviceId':
|
||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
'title': action.actionExecutor == 'delay'
|
||||
? 'Delay'
|
||||
: (matchingDevice.name ?? 'Device'),
|
||||
'productType': action.productType,
|
||||
'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
||||
};
|
||||
// final cardData = {
|
||||
// 'entityId': action.entityId,
|
||||
// 'uniqueCustomId': const Uuid().v4(),
|
||||
// 'deviceId':
|
||||
// action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
// 'title': action.actionExecutor == 'delay'
|
||||
// ? 'Delay'
|
||||
// : (matchingDevice.name ?? 'Device'),
|
||||
// 'productType': action.productType,
|
||||
// 'imagePath': matchingDevice.getDefaultIcon(action.productType),
|
||||
// };
|
||||
|
||||
final functions = matchingDevice.functions;
|
||||
// final functions = matchingDevice.functions;
|
||||
|
||||
if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||
final functionCode = action.executorProperty!.functionCode;
|
||||
for (var function in functions) {
|
||||
if (function.code == functionCode) {
|
||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
DeviceFunctionData(
|
||||
entityId: action.entityId,
|
||||
functionCode: functionCode ?? '',
|
||||
value: action.executorProperty!.functionValue,
|
||||
operationName: function.operationName,
|
||||
),
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (action.actionExecutor == 'delay') {
|
||||
final delayFunction = DelayFunction(
|
||||
deviceId: action.entityId,
|
||||
deviceName: 'Delay',
|
||||
);
|
||||
currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
DeviceFunctionData(
|
||||
entityId: action.entityId,
|
||||
functionCode: 'delay',
|
||||
value: action.executorProperty?.delaySeconds ?? 0,
|
||||
operationName: delayFunction.operationName,
|
||||
),
|
||||
];
|
||||
}
|
||||
// if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||
// final functionCode = action.executorProperty!.functionCode;
|
||||
// for (var function in functions) {
|
||||
// if (function.code == functionCode) {
|
||||
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
// DeviceFunctionData(
|
||||
// entityId: action.entityId,
|
||||
// functionCode: functionCode ?? '',
|
||||
// value: action.executorProperty!.functionValue,
|
||||
// operationName: function.operationName,
|
||||
// ),
|
||||
// ];
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// } else if (action.actionExecutor == 'delay') {
|
||||
// final delayFunction = DelayFunction(
|
||||
// deviceId: action.entityId,
|
||||
// deviceName: 'Delay',
|
||||
// );
|
||||
// currentFunctions[cardData['uniqueCustomId'] ?? ''] = [
|
||||
// DeviceFunctionData(
|
||||
// entityId: action.entityId,
|
||||
// functionCode: 'delay',
|
||||
// value: action.executorProperty?.delaySeconds ?? 0,
|
||||
// operationName: delayFunction.operationName,
|
||||
// ),
|
||||
// ];
|
||||
// }
|
||||
|
||||
return cardData;
|
||||
}).toList();
|
||||
// return cardData;
|
||||
// }).toList();
|
||||
|
||||
return (thenItems, ifItems, currentFunctions);
|
||||
}
|
||||
// return (thenItems, ifItems, currentFunctions);
|
||||
// }
|
||||
|
||||
Future<void> _onGetSceneDetails(
|
||||
GetSceneDetails event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onGetSceneDetails(GetSceneDetails event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
emit(state.copyWith(
|
||||
isLoading: true,
|
||||
@ -719,42 +684,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
? '${action.entityId}_automation'
|
||||
: action.actionExecutor == 'delay'
|
||||
? '${action.entityId}_delay'
|
||||
: action.entityId;
|
||||
: const Uuid().v4();
|
||||
|
||||
if (!deviceCards.containsKey(deviceId)) {
|
||||
deviceCards[deviceId] = {
|
||||
'entityId': action.entityId,
|
||||
'deviceId':
|
||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
'uniqueCustomId':
|
||||
action.type == 'automation' || action.actionExecutor == 'delay'
|
||||
? const Uuid().v4()
|
||||
: action.entityId,
|
||||
'title': action.actionExecutor == 'delay'
|
||||
? 'Delay'
|
||||
: action.type == 'automation'
|
||||
? action.name ?? 'Automation'
|
||||
: (matchingDevice?.name ?? 'Device'),
|
||||
'productType': action.productType,
|
||||
'functions': matchingDevice?.functions,
|
||||
'imagePath': action.type == 'automation'
|
||||
? Assets.automation
|
||||
: action.actionExecutor == 'delay'
|
||||
? Assets.delay
|
||||
: matchingDevice?.getDefaultIcon(action.productType),
|
||||
'device': matchingDevice,
|
||||
'name': action.name,
|
||||
'type': action.type,
|
||||
};
|
||||
}
|
||||
// if (!deviceCards.containsKey(deviceId)) {
|
||||
deviceCards[deviceId] = {
|
||||
'entityId': action.entityId,
|
||||
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
'uniqueCustomId': action.type == 'automation' || action.actionExecutor == 'delay'
|
||||
? action.entityId
|
||||
: const Uuid().v4(),
|
||||
'title': action.actionExecutor == 'delay'
|
||||
? 'Delay'
|
||||
: action.type == 'automation'
|
||||
? action.name ?? 'Automation'
|
||||
: (matchingDevice?.name ?? 'Device'),
|
||||
'productType': action.productType,
|
||||
'functions': matchingDevice?.functions,
|
||||
'imagePath': action.type == 'automation'
|
||||
? Assets.automation
|
||||
: action.actionExecutor == 'delay'
|
||||
? Assets.delay
|
||||
: matchingDevice?.getDefaultIcon(action.productType),
|
||||
'device': matchingDevice,
|
||||
'name': action.name,
|
||||
'type': action.type,
|
||||
'tag': matchingDevice?.deviceTags?.isNotEmpty ?? false
|
||||
? matchingDevice?.deviceTags![0].name ?? ''
|
||||
: '',
|
||||
'subSpace': matchingDevice?.deviceSubSpace?.subspaceName ?? '',
|
||||
};
|
||||
// }
|
||||
|
||||
final cardData = deviceCards[deviceId]!;
|
||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||
|
||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||
updatedFunctions[uniqueCustomId] = [];
|
||||
}
|
||||
|
||||
if (action.type == 'automation') {
|
||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||
updatedFunctions[uniqueCustomId] = [];
|
||||
}
|
||||
updatedFunctions[uniqueCustomId]!.add(
|
||||
DeviceFunctionData(
|
||||
entityId: action.entityId,
|
||||
@ -764,14 +732,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
),
|
||||
);
|
||||
// emit(state.copyWith(automationActionExecutor: action.actionExecutor));
|
||||
} else if (action.executorProperty != null &&
|
||||
action.actionExecutor != 'delay') {
|
||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||
updatedFunctions[uniqueCustomId] = [];
|
||||
}
|
||||
final functions = matchingDevice?.functions;
|
||||
} else if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||
final functions = matchingDevice?.functions ?? [];
|
||||
final functionCode = action.executorProperty?.functionCode;
|
||||
for (DeviceFunction function in functions ?? []) {
|
||||
for (DeviceFunction function in functions) {
|
||||
if (function.code == functionCode) {
|
||||
updatedFunctions[uniqueCustomId]!.add(
|
||||
DeviceFunctionData(
|
||||
@ -785,9 +749,6 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
} else if (action.actionExecutor == 'delay') {
|
||||
if (!updatedFunctions.containsKey(uniqueCustomId)) {
|
||||
updatedFunctions[uniqueCustomId] = [];
|
||||
}
|
||||
final delayFunction = DelayFunction(
|
||||
deviceId: action.entityId,
|
||||
deviceName: 'Delay',
|
||||
@ -837,8 +798,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onResetRoutineState(
|
||||
ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||
FutureOr<void> _onResetRoutineState(ResetRoutineState event, Emitter<RoutineState> emit) {
|
||||
emit(state.copyWith(
|
||||
ifItems: [],
|
||||
thenItems: [],
|
||||
@ -861,6 +821,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
isUpdate: false,
|
||||
createRoutineView: false));
|
||||
}
|
||||
|
||||
FutureOr<void> _deleteScene(DeleteScene event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
final projectId = await ProjectManager.getProjectUUID() ?? '';
|
||||
@ -900,7 +861,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FutureOr<void> _deleteAutomation(DeleteAutomation event, Emitter<RoutineState> emit) {
|
||||
// try {
|
||||
// emit(state.copyWith(isLoading: true));
|
||||
@ -915,8 +876,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
// }
|
||||
// }
|
||||
|
||||
FutureOr<void> _fetchDevices(
|
||||
FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
||||
FutureOr<void> _fetchDevices(FetchDevicesInRoutine event, Emitter<RoutineState> emit) async {
|
||||
emit(state.copyWith(isLoading: true));
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
@ -925,21 +885,17 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
var createRoutineBloc = context.read<CreateRoutineBloc>();
|
||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||
|
||||
if (createRoutineBloc.selectedSpaceId == '' &&
|
||||
createRoutineBloc.selectedCommunityId == '') {
|
||||
if (createRoutineBloc.selectedSpaceId == '' && createRoutineBloc.selectedCommunityId == '') {
|
||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
List<String> spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
for (var spaceId in spacesList) {
|
||||
devices.addAll(await DevicesManagementApi()
|
||||
.fetchDevices(communityId, spaceId, projectUuid));
|
||||
devices.addAll(
|
||||
await DevicesManagementApi().fetchDevices(communityId, spaceId, projectUuid));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
devices.addAll(await DevicesManagementApi().fetchDevices(
|
||||
createRoutineBloc.selectedCommunityId,
|
||||
createRoutineBloc.selectedSpaceId,
|
||||
projectUuid));
|
||||
createRoutineBloc.selectedCommunityId, createRoutineBloc.selectedSpaceId, projectUuid));
|
||||
}
|
||||
|
||||
emit(state.copyWith(isLoading: false, devices: devices));
|
||||
@ -948,8 +904,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onUpdateScene(
|
||||
UpdateScene event, Emitter<RoutineState> emit) async {
|
||||
FutureOr<void> _onUpdateScene(UpdateScene event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
// Check if first action is delay
|
||||
// if (_isFirstActionDelay(state.thenItems)) {
|
||||
@ -963,8 +918,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
|
||||
if (_isLastActionDelay(state.thenItems)) {
|
||||
emit(state.copyWith(
|
||||
errorMessage:
|
||||
'A delay condition cannot be the only or the last action',
|
||||
errorMessage: 'A delay condition cannot be the only or the last action',
|
||||
isLoading: false,
|
||||
));
|
||||
return;
|
||||
@ -1017,8 +971,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
actions: actions,
|
||||
);
|
||||
|
||||
final result =
|
||||
await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||
final result = await SceneApi.updateScene(createSceneModel, state.sceneId ?? '');
|
||||
if (result['success']) {
|
||||
add(ResetRoutineState());
|
||||
add(const LoadScenes());
|
||||
@ -1037,8 +990,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onUpdateAutomation(
|
||||
UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||
FutureOr<void> _onUpdateAutomation(UpdateAutomation event, Emitter<RoutineState> emit) async {
|
||||
try {
|
||||
if (state.routineName == null || state.routineName!.isEmpty) {
|
||||
emit(state.copyWith(
|
||||
@ -1203,21 +1155,25 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
),
|
||||
);
|
||||
|
||||
final deviceId = condition.entityId;
|
||||
final deviceId = const Uuid().v4();
|
||||
|
||||
if (!deviceIfCards.containsKey(deviceId)) {
|
||||
deviceIfCards[deviceId] = {
|
||||
'entityId': condition.entityId,
|
||||
'deviceId': condition.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'title': matchingDevice.name ?? 'Device',
|
||||
'productType': condition.productType,
|
||||
'functions': matchingDevice.functions,
|
||||
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
||||
'device': matchingDevice,
|
||||
'type': 'condition',
|
||||
};
|
||||
}
|
||||
// if (!deviceIfCards.containsKey(deviceId)) {
|
||||
deviceIfCards[deviceId] = {
|
||||
'entityId': condition.entityId,
|
||||
'deviceId': condition.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'title': matchingDevice.name ?? 'Device',
|
||||
'productType': condition.productType,
|
||||
'functions': matchingDevice.functions,
|
||||
'imagePath': matchingDevice.getDefaultIcon(condition.productType),
|
||||
'device': matchingDevice,
|
||||
'type': 'condition',
|
||||
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
||||
? matchingDevice.deviceTags![0].name
|
||||
: '',
|
||||
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
||||
};
|
||||
// }
|
||||
|
||||
final cardData = deviceIfCards[deviceId]!;
|
||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||
@ -1253,37 +1209,38 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
),
|
||||
);
|
||||
|
||||
final deviceId = action.actionExecutor == 'delay'
|
||||
? '${action.entityId}_delay'
|
||||
: action.entityId;
|
||||
final deviceId = const Uuid().v4();
|
||||
|
||||
if (!deviceThenCards.containsKey(deviceId)) {
|
||||
deviceThenCards[deviceId] = {
|
||||
'entityId': action.entityId,
|
||||
'deviceId':
|
||||
action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'title': action.actionExecutor == 'delay'
|
||||
? 'Delay'
|
||||
: (action.type == 'scene' || action.type == 'automation')
|
||||
? action.name
|
||||
: (matchingDevice.name ?? 'Device'),
|
||||
'productType': action.productType,
|
||||
'functions': matchingDevice.functions,
|
||||
'imagePath': action.actionExecutor == 'delay'
|
||||
? Assets.delay
|
||||
: action.type == 'automation'
|
||||
? Assets.automation
|
||||
: matchingDevice.getDefaultIcon(action.productType),
|
||||
'device': matchingDevice,
|
||||
'type': action.type == 'scene'
|
||||
? 'scene'
|
||||
: action.type == 'automation'
|
||||
? 'automation'
|
||||
: 'action',
|
||||
'icon': action.icon ?? ''
|
||||
};
|
||||
}
|
||||
// if (!deviceThenCards.containsKey(deviceId)) {
|
||||
deviceThenCards[deviceId] = {
|
||||
'entityId': action.entityId,
|
||||
'deviceId': action.actionExecutor == 'delay' ? 'delay' : action.entityId,
|
||||
'uniqueCustomId': const Uuid().v4(),
|
||||
'title': action.actionExecutor == 'delay'
|
||||
? 'Delay'
|
||||
: (action.type == 'scene' || action.type == 'automation')
|
||||
? action.name
|
||||
: (matchingDevice.name ?? 'Device'),
|
||||
'productType': action.productType,
|
||||
'functions': matchingDevice.functions,
|
||||
'imagePath': action.actionExecutor == 'delay'
|
||||
? Assets.delay
|
||||
: action.type == 'automation'
|
||||
? Assets.automation
|
||||
: matchingDevice.getDefaultIcon(action.productType),
|
||||
'device': matchingDevice,
|
||||
'type': action.type == 'scene'
|
||||
? 'scene'
|
||||
: action.type == 'automation'
|
||||
? 'automation'
|
||||
: 'action',
|
||||
'icon': action.icon ?? '',
|
||||
'tag': matchingDevice.deviceTags?.isNotEmpty ?? false
|
||||
? matchingDevice.deviceTags![0].name
|
||||
: '',
|
||||
'subSpace': matchingDevice.deviceSubSpace?.subspaceName ?? '',
|
||||
};
|
||||
// }
|
||||
|
||||
final cardData = deviceThenCards[deviceId]!;
|
||||
final uniqueCustomId = cardData['uniqueCustomId'].toString();
|
||||
@ -1292,8 +1249,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
updatedFunctions[uniqueCustomId] = [];
|
||||
}
|
||||
|
||||
if (action.executorProperty != null &&
|
||||
action.actionExecutor != 'delay') {
|
||||
if (action.executorProperty != null && action.actionExecutor != 'delay') {
|
||||
final functions = matchingDevice.functions;
|
||||
final functionCode = action.executorProperty!.functionCode;
|
||||
for (var function in functions) {
|
||||
@ -1335,14 +1291,10 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
final ifItems = deviceIfCards.values
|
||||
.where((card) => card['type'] == 'condition')
|
||||
.toList();
|
||||
final ifItems = deviceIfCards.values.where((card) => card['type'] == 'condition').toList();
|
||||
final thenItems = deviceThenCards.values
|
||||
.where((card) =>
|
||||
card['type'] == 'action' ||
|
||||
card['type'] == 'automation' ||
|
||||
card['type'] == 'scene')
|
||||
card['type'] == 'action' || card['type'] == 'automation' || card['type'] == 'scene')
|
||||
.toList();
|
||||
|
||||
emit(state.copyWith(
|
||||
@ -1364,8 +1316,7 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onSceneTrigger(
|
||||
SceneTrigger event, Emitter<RoutineState> emit) async {
|
||||
Future<void> _onSceneTrigger(SceneTrigger event, Emitter<RoutineState> emit) async {
|
||||
emit(state.copyWith(loadingSceneId: event.sceneId));
|
||||
|
||||
try {
|
||||
@ -1407,29 +1358,24 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
|
||||
|
||||
if (success) {
|
||||
final updatedAutomations = await SceneApi.getAutomationByUnitId(
|
||||
event.automationStatusUpdate.spaceUuid,
|
||||
event.communityId,
|
||||
projectId);
|
||||
event.automationStatusUpdate.spaceUuid, event.communityId, projectId);
|
||||
|
||||
// Remove from loading set safely
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||
..remove(event.automationId);
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||
|
||||
emit(state.copyWith(
|
||||
automations: updatedAutomations,
|
||||
loadingAutomationIds: updatedLoadingIds,
|
||||
));
|
||||
} else {
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||
..remove(event.automationId);
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||
emit(state.copyWith(
|
||||
loadingAutomationIds: updatedLoadingIds,
|
||||
errorMessage: 'Update failed',
|
||||
));
|
||||
}
|
||||
} catch (e) {
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}
|
||||
..remove(event.automationId);
|
||||
final updatedLoadingIds = {...state.loadingAutomationIds!}..remove(event.automationId);
|
||||
emit(state.copyWith(
|
||||
loadingAutomationIds: updatedLoadingIds,
|
||||
errorMessage: 'Update error: ${e.toString()}',
|
||||
|
@ -63,7 +63,11 @@ class _CreateNewRoutinesDialogState extends State<CreateNewRoutinesDialog> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 15, right: 15),
|
||||
child: CommunityDropdown(
|
||||
communities: _bloc.communities,
|
||||
communities: _bloc.communities..sort(
|
||||
(a, b) => a.name.toLowerCase().compareTo(
|
||||
b.name.toLowerCase(),
|
||||
),
|
||||
),
|
||||
selectedValue: _selectedCommunity,
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
|
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ac_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/gateway/gateway_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/one_gang_switch_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/three_gang_switch_dialog.dart';
|
||||
@ -98,6 +99,15 @@ class DeviceDialogHelper {
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: data['uniqueCustomId'],
|
||||
removeComparetors: removeComparetors);
|
||||
case 'CPS':
|
||||
return CeilingSensorHelper.showCeilingSensorDialog(
|
||||
context: context,
|
||||
functions: functions,
|
||||
device: data['device'],
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
uniqueCustomId: data['uniqueCustomId'],
|
||||
dialogType: dialogType,
|
||||
);
|
||||
case 'GW':
|
||||
return GatewayHelper.showGatewayFunctionsDialog(
|
||||
context: context,
|
||||
|
@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
@ -14,13 +14,18 @@ class SaveRoutineHelper {
|
||||
static Future<void> showSaveRoutineDialog(BuildContext context) async {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
builder: (context) {
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
final selectedConditionLabel = state.selectedAutomationOperator == 'and'
|
||||
? 'All Conditions are met'
|
||||
: 'Any Condition is met';
|
||||
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: Container(
|
||||
width: 600,
|
||||
width: context.screenWidth * 0.5,
|
||||
height: 500,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
@ -28,146 +33,42 @@ class SaveRoutineHelper {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DialogHeader('Create a scene: ${state.routineName ?? ""}'),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Left side - IF
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'IF:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (state.isTabToRun)
|
||||
ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
Assets.tabToRun,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: const Text('Tab to run'),
|
||||
),
|
||||
if (state.isAutomation)
|
||||
...state.ifItems.map((item) {
|
||||
final functions =
|
||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||
return ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
item['imagePath'],
|
||||
width: 22,
|
||||
height: 22,
|
||||
),
|
||||
title:
|
||||
Text(item['title'], style: const TextStyle(fontSize: 14)),
|
||||
subtitle: Wrap(
|
||||
children: functions
|
||||
.map((f) => Text(
|
||||
'${f.operationName}: ${f.value}, ',
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.grayColor, fontSize: 8),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
'Create a scene: ${state.routineName ?? ""}',
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
// Right side - THEN items
|
||||
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'THEN:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
...state.thenItems.map((item) {
|
||||
final functions =
|
||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||
return ListTile(
|
||||
leading: item['type'] == 'tap_to_run' || item['type'] == 'scene'
|
||||
? Image.memory(
|
||||
base64Decode(item['icon']),
|
||||
width: 22,
|
||||
height: 22,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
item['imagePath'],
|
||||
width: 22,
|
||||
height: 22,
|
||||
),
|
||||
title: Text(
|
||||
item['title'],
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
subtitle: Wrap(
|
||||
children: functions
|
||||
.map((f) => Text(
|
||||
'${f.operationName}: ${f.value}, ',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor, fontSize: 8),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
_buildDivider(),
|
||||
_buildListsLabelRow(selectedConditionLabel),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsetsDirectional.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
spacing: 24,
|
||||
children: [
|
||||
_buildIfConditions(state, context),
|
||||
Container(
|
||||
width: 1,
|
||||
color: ColorsManager.greyColor.withValues(alpha: 0.8),
|
||||
),
|
||||
),
|
||||
],
|
||||
_buildThenActions(state, context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// if (state.errorMessage != null || state.errorMessage!.isNotEmpty)
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: Text(
|
||||
// state.errorMessage!,
|
||||
// style: const TextStyle(color: Colors.red),
|
||||
// ),
|
||||
// ),
|
||||
DialogFooter(
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: () async {
|
||||
if (state.isAutomation) {
|
||||
if (state.isUpdate ?? false) {
|
||||
context.read<RoutineBloc>().add(const UpdateAutomation());
|
||||
} else {
|
||||
context.read<RoutineBloc>().add(const CreateAutomationEvent());
|
||||
}
|
||||
} else {
|
||||
if (state.isUpdate ?? false) {
|
||||
context.read<RoutineBloc>().add(const UpdateScene());
|
||||
} else {
|
||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||
}
|
||||
}
|
||||
// if (state.errorMessage == null || state.errorMessage!.isEmpty) {
|
||||
Navigator.pop(context);
|
||||
// }
|
||||
},
|
||||
isConfirmEnabled: true,
|
||||
),
|
||||
_buildDivider(),
|
||||
const SizedBox(height: 8),
|
||||
_buildDialogFooter(context, state),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -177,4 +78,245 @@ class SaveRoutineHelper {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static Container _buildDivider() {
|
||||
return Container(
|
||||
height: 1,
|
||||
width: double.infinity,
|
||||
color: ColorsManager.greyColor,
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildListsLabelRow(String selectedConditionLabel) {
|
||||
const textStyle = TextStyle(
|
||||
fontSize: 16,
|
||||
);
|
||||
return Container(
|
||||
color: ColorsManager.backgroundColor.withValues(alpha: 0.5),
|
||||
padding: const EdgeInsetsDirectional.all(20),
|
||||
child: Row(
|
||||
spacing: 16,
|
||||
children: [
|
||||
Expanded(child: Text('IF: $selectedConditionLabel', style: textStyle)),
|
||||
const Expanded(child: Text('THEN:', style: textStyle)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildDialogFooter(BuildContext context, RoutineState state) {
|
||||
return Row(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
DialogFooterButton(
|
||||
text: 'Cancel',
|
||||
onTap: () => Navigator.pop(context),
|
||||
),
|
||||
DialogFooterButton(
|
||||
text: 'Confirm',
|
||||
onTap: () {
|
||||
if (state.isAutomation) {
|
||||
if (state.isUpdate ?? false) {
|
||||
context.read<RoutineBloc>().add(const UpdateAutomation());
|
||||
} else {
|
||||
context.read<RoutineBloc>().add(const CreateAutomationEvent());
|
||||
}
|
||||
} else {
|
||||
if (state.isUpdate ?? false) {
|
||||
context.read<RoutineBloc>().add(const UpdateScene());
|
||||
} else {
|
||||
context.read<RoutineBloc>().add(const CreateSceneEvent());
|
||||
}
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
textColor: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildThenActions(RoutineState state, BuildContext context) {
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
// shrinkWrap: true,
|
||||
children: state.thenItems.map((item) {
|
||||
final functions = state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||
return functionRow(item, context, functions);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildIfConditions(RoutineState state, BuildContext context) {
|
||||
return Expanded(
|
||||
child: ListView(
|
||||
// shrinkWrap: true,
|
||||
children: [
|
||||
if (state.isTabToRun)
|
||||
ListTile(
|
||||
leading: SvgPicture.asset(
|
||||
Assets.tabToRun,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
title: const Text('Tab to run'),
|
||||
),
|
||||
if (state.isAutomation)
|
||||
...state.ifItems.map((item) {
|
||||
final functions =
|
||||
state.selectedFunctions[item['uniqueCustomId']] ?? [];
|
||||
return functionRow(item, context, functions);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget functionRow(
|
||||
dynamic item,
|
||||
BuildContext context,
|
||||
List<DeviceFunctionData> functions,
|
||||
) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 6),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 17,
|
||||
children: [
|
||||
Container(
|
||||
width: 22,
|
||||
height: 22,
|
||||
padding: const EdgeInsetsDirectional.all(4),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: item['type'] == 'tap_to_run' || item['type'] == 'scene'
|
||||
? Image.memory(
|
||||
base64Decode(item['icon']),
|
||||
width: 12,
|
||||
height: 22,
|
||||
fit: BoxFit.scaleDown,
|
||||
)
|
||||
: SvgPicture.asset(
|
||||
item['imagePath'],
|
||||
width: 12,
|
||||
height: 12,
|
||||
fit: BoxFit.scaleDown,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 2,
|
||||
children: [
|
||||
Text(
|
||||
item['title'],
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 15,
|
||||
color: ColorsManager.textPrimaryColor,
|
||||
),
|
||||
),
|
||||
Wrap(
|
||||
runSpacing: 16,
|
||||
spacing: 4,
|
||||
children: functions
|
||||
.map(
|
||||
(function) => Text(
|
||||
'${function.operationName}: ${function.value}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 8,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 3,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 2,
|
||||
children: [
|
||||
Visibility(
|
||||
visible: item['tag'] != null && item['tag'] != '',
|
||||
child: Row(
|
||||
spacing: 2,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 8,
|
||||
height: 8,
|
||||
child: SvgPicture.asset(
|
||||
Assets.deviceTagIcon,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item['tag'] ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: item['subSpace'] != null && item['subSpace'] != '',
|
||||
child: Row(
|
||||
spacing: 2,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 8,
|
||||
height: 8,
|
||||
child: SvgPicture.asset(
|
||||
Assets.spaceLocationIcon,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
item['subSpace'] ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
889
lib/pages/routines/models/ceiling_presence_sensor_functions.dart
Normal file
@ -0,0 +1,889 @@
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CpsOperationalValue {
|
||||
final String icon;
|
||||
final String description;
|
||||
final dynamic value;
|
||||
|
||||
CpsOperationalValue({
|
||||
required this.icon,
|
||||
required this.description,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class CpsFunctions extends DeviceFunction<CpsOperationalValue> {
|
||||
CpsFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.code,
|
||||
required super.operationName,
|
||||
required super.icon,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
final String type;
|
||||
|
||||
List<CpsOperationalValue> getOperationalValues();
|
||||
}
|
||||
|
||||
final class CpsRadarSwitchFunction extends CpsFunctions {
|
||||
CpsRadarSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'radar_switch',
|
||||
operationName: 'Radar Switch',
|
||||
icon: Assets.acPower,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() => [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: "ON",
|
||||
value: true,
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: "OFF",
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final class CpsSpatialParameterSwitchFunction extends CpsFunctions {
|
||||
CpsSpatialParameterSwitchFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'space_para_switch',
|
||||
operationName: 'Spatial Parameter Switch',
|
||||
icon: Assets.acPower,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() => [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPower,
|
||||
description: "ON",
|
||||
value: true,
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsAcPowerOFF,
|
||||
description: "OFF",
|
||||
value: false,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
final class CpsSensitivityFunction extends CpsFunctions {
|
||||
CpsSensitivityFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 1,
|
||||
max = 10,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'sensitivity',
|
||||
operationName: 'Sensitivity',
|
||||
icon: Assets.sensitivity,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
static const _images = <String>[
|
||||
Assets.sensitivityFeature1,
|
||||
Assets.sensitivityFeature1,
|
||||
Assets.sensitivityFeature2,
|
||||
Assets.sensitivityFeature3,
|
||||
Assets.sensitivityFeature4,
|
||||
Assets.sensitivityFeature5,
|
||||
Assets.sensitivityFeature6,
|
||||
Assets.sensitivityFeature7,
|
||||
Assets.sensitivityFeature8,
|
||||
Assets.sensitivityFeature9,
|
||||
Assets.sensitivityFeature9,
|
||||
];
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final values = <CpsOperationalValue>[];
|
||||
for (var value = min; value <= max; value += step) {
|
||||
values.add(
|
||||
CpsOperationalValue(
|
||||
icon: _images[value],
|
||||
description: '$value',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMovingSpeedFunction extends CpsFunctions {
|
||||
CpsMovingSpeedFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 32,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'moving_speed',
|
||||
operationName: 'Moving Speed',
|
||||
icon: Assets.speedoMeter,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.speedoMeter,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpatialStaticValueFunction extends CpsFunctions {
|
||||
CpsSpatialStaticValueFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'space_static_val',
|
||||
operationName: 'Spacial Static Value',
|
||||
icon: Assets.spatialStaticValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.spatialStaticValue,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpatialMotionValueFunction extends CpsFunctions {
|
||||
CpsSpatialMotionValueFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'space_move_val',
|
||||
operationName: 'Spatial Motion Value',
|
||||
icon: Assets.spatialMotionValue,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.spatialMotionValue,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMaxDistanceOfDetectionFunction extends CpsFunctions {
|
||||
CpsMaxDistanceOfDetectionFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 10.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'moving_max_dis',
|
||||
operationName: 'Maximum Distance Of Detection',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMaxDistanceOfStaticDetectionFunction extends CpsFunctions {
|
||||
CpsMaxDistanceOfStaticDetectionFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 10.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'static_max_dis',
|
||||
operationName: 'Maximum Distance Of Static Detection',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsDetectionRangeFunction extends CpsFunctions {
|
||||
CpsDetectionRangeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 25.5,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'moving_range',
|
||||
operationName: 'Detection Range',
|
||||
icon: Assets.farDetection,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.farDetection,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsDistanceOfMovingObjectsFunction extends CpsFunctions {
|
||||
CpsDistanceOfMovingObjectsFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 25.5,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'presence_range',
|
||||
operationName: 'Distance Of Moving Objects',
|
||||
icon: Assets.currentDistanceIcon,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.currentDistanceIcon,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsPresenceJudgementThrsholdFunction extends CpsFunctions {
|
||||
CpsPresenceJudgementThrsholdFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 5,
|
||||
super(
|
||||
code: 'presence_reference',
|
||||
operationName: 'Presence Judgement Threshold',
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionAmplitudeTriggerThresholdFunction extends CpsFunctions {
|
||||
CpsMotionAmplitudeTriggerThresholdFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0,
|
||||
max = 255,
|
||||
step = 5,
|
||||
super(
|
||||
code: 'moving_reference',
|
||||
operationName: 'Motion Amplitude Trigger Threshold',
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
);
|
||||
|
||||
final int min;
|
||||
final int max;
|
||||
final int step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return List.generate(
|
||||
(max - min) ~/ step + 1,
|
||||
(index) => CpsOperationalValue(
|
||||
icon: Assets.presenceJudgementThrshold,
|
||||
description: '${min + (index * step)}',
|
||||
value: min + (index * step),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsPerpetualBoundaryFunction extends CpsFunctions {
|
||||
CpsPerpetualBoundaryFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.00,
|
||||
max = 5.00,
|
||||
step = 0.50,
|
||||
super(
|
||||
code: 'perceptual_boundary',
|
||||
operationName: 'Perpetual Boundary',
|
||||
icon: Assets.boundary,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.boundary,
|
||||
description: '${value.toStringAsFixed(1)}M',
|
||||
value: value + 1200,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionTriggerBoundaryFunction extends CpsFunctions {
|
||||
CpsMotionTriggerBoundaryFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 5.0,
|
||||
step = 0.5,
|
||||
super(
|
||||
code: 'moving_boundary',
|
||||
operationName: 'Motion Trigger Boundary',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(1)} M',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionTriggerTimeFunction extends CpsFunctions {
|
||||
CpsMotionTriggerTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 2.0,
|
||||
step = 0.1,
|
||||
super(
|
||||
code: 'moving_rigger_time',
|
||||
operationName: 'Motion Trigger Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(3)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMotionToStaticTimeFunction extends CpsFunctions {
|
||||
CpsMotionToStaticTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 50.0,
|
||||
step = 1.0,
|
||||
super(
|
||||
code: 'moving_static_time',
|
||||
operationName: 'Motion To Static Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(0)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsEnteringNoBodyStateTimeFunction extends CpsFunctions {
|
||||
CpsEnteringNoBodyStateTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 0.0,
|
||||
max = 300.0,
|
||||
step = 5.0,
|
||||
super(
|
||||
code: 'none_body_time',
|
||||
operationName: 'Entering Nobody State Time',
|
||||
icon: Assets.motionMeter,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: '${value.toStringAsFixed(0)} sec',
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSelfTestResultFunctions extends CpsFunctions {
|
||||
CpsSelfTestResultFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'checking_result',
|
||||
operationName: 'Self-Test Result',
|
||||
icon: Assets.selfTestResult,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing',
|
||||
icon: Assets.selfTestResult,
|
||||
value: 'check',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Success',
|
||||
icon: Assets.selfTestingSuccess,
|
||||
value: 'check_success',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Failure',
|
||||
icon: Assets.selfTestingFailure,
|
||||
value: 'check_failure',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Self Testing Timeout',
|
||||
icon: Assets.selfTestingTimeout,
|
||||
value: 'check_timeout',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Communication Fault',
|
||||
icon: Assets.communicationFault,
|
||||
value: 'communication_fault',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Radar Fault',
|
||||
icon: Assets.radarFault,
|
||||
value: 'radar_fault',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsNobodyTimeFunction extends CpsFunctions {
|
||||
CpsNobodyTimeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'nobody_time',
|
||||
operationName: 'Entering Nobody Time',
|
||||
icon: Assets.assetsNobodyTime,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: 'None',
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '10sec',
|
||||
value: '10s',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '30sec',
|
||||
value: '30s',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '1min',
|
||||
value: '1min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '2min',
|
||||
value: '2min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '5min',
|
||||
value: '5min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '10min',
|
||||
value: '10min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '30min',
|
||||
value: '30min',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.assetsNobodyTime,
|
||||
description: '1hour',
|
||||
value: '1hr',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsMovementFunctions extends CpsFunctions {
|
||||
CpsMovementFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'body_movement',
|
||||
operationName: 'Movement',
|
||||
icon: Assets.motion,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'None',
|
||||
icon: Assets.nobodyTime,
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Close To',
|
||||
icon: Assets.closeToMotion,
|
||||
value: 'close_to',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Far Away',
|
||||
icon: Assets.farAwayMotion,
|
||||
value: 'far_away',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsCustomModeFunction extends CpsFunctions {
|
||||
CpsCustomModeFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'custom_mode',
|
||||
operationName: 'Custom Mode',
|
||||
icon: Assets.cpsCustomMode,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode1,
|
||||
description: 'Mode 1',
|
||||
value: 'mode1',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode2,
|
||||
description: 'Mode 2',
|
||||
value: 'mode2',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode3,
|
||||
description: 'Mode 3',
|
||||
value: 'mode3',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.cpsMode4,
|
||||
description: 'Mode 4',
|
||||
value: 'mode4',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSpaceTypeFunctions extends CpsFunctions {
|
||||
CpsSpaceTypeFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'scene',
|
||||
operationName: 'Space Type',
|
||||
icon: Assets.spaceType,
|
||||
);
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
description: 'Office',
|
||||
icon: Assets.office,
|
||||
value: 'office',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Parlour',
|
||||
icon: Assets.parlour,
|
||||
value: 'parlour',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Bathroom',
|
||||
icon: Assets.bathroom,
|
||||
value: 'bathroom',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'Bedroom',
|
||||
icon: Assets.bedroom,
|
||||
value: 'bedroom',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
description: 'DIY',
|
||||
icon: Assets.dyi,
|
||||
value: 'dyi',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
class CpsPresenceStatusFunctions extends CpsFunctions {
|
||||
CpsPresenceStatusFunctions({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : super(
|
||||
code: 'presence_state',
|
||||
operationName: 'Presence Status',
|
||||
icon: Assets.presenceSensor,
|
||||
);
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
return [
|
||||
CpsOperationalValue(
|
||||
icon: Assets.nobodyTime,
|
||||
description: 'None',
|
||||
value: 'none',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.presenceState,
|
||||
description: 'Presence',
|
||||
value: 'presence',
|
||||
),
|
||||
CpsOperationalValue(
|
||||
icon: Assets.motion,
|
||||
description: 'Motion',
|
||||
value: 'motion',
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
final class CpsSportsParaFunction extends CpsFunctions {
|
||||
CpsSportsParaFunction({
|
||||
required super.deviceId,
|
||||
required super.deviceName,
|
||||
required super.type,
|
||||
}) : min = 1,
|
||||
max = 100,
|
||||
step = 1,
|
||||
super(
|
||||
code: 'sports_para',
|
||||
operationName: 'Sports Para',
|
||||
icon: Assets.sportsPara,
|
||||
);
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
final double step;
|
||||
|
||||
@override
|
||||
List<CpsOperationalValue> getOperationalValues() {
|
||||
final count = ((max - min) / step).round() + 1;
|
||||
return List.generate(
|
||||
count,
|
||||
(index) {
|
||||
final value = (min + (index * step));
|
||||
return CpsOperationalValue(
|
||||
icon: Assets.motionMeter,
|
||||
description: value.toStringAsFixed(0),
|
||||
value: value,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/create_routine_bloc/create_routine_event.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/create_new_routines/create_new_routines.dart';
|
||||
import 'package:syncrow_web/pages/routines/view/create_new_routine_view.dart';
|
||||
@ -9,6 +9,7 @@ import 'package:syncrow_web/pages/routines/widgets/main_routine_view/fetch_routi
|
||||
import 'package:syncrow_web/pages/routines/widgets/main_routine_view/routine_view_card.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class RoutinesView extends StatefulWidget {
|
||||
const RoutinesView({super.key});
|
||||
@ -27,10 +28,10 @@ class _RoutinesViewState extends State<RoutinesView> {
|
||||
if (result == null) return;
|
||||
final communityId = result['community'];
|
||||
final spaceId = result['space'];
|
||||
final _bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
||||
final bloc = BlocProvider.of<CreateRoutineBloc>(context);
|
||||
final routineBloc = context.read<RoutineBloc>();
|
||||
_bloc.add(SaveCommunityIdAndSpaceIdEvent(
|
||||
communityID: communityId, spaceID: spaceId));
|
||||
bloc.add(
|
||||
SaveCommunityIdAndSpaceIdEvent(communityID: communityId, spaceID: spaceId));
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
routineBloc.add(const CreateNewRoutineViewEvent(createRoutineView: true));
|
||||
}
|
||||
@ -49,49 +50,47 @@ class _RoutinesViewState extends State<RoutinesView> {
|
||||
child: SpaceTreeView(
|
||||
onSelect: () => context.read<RoutineBloc>()
|
||||
..add(const LoadScenes())
|
||||
..add(const LoadAutomation()),
|
||||
..add(const LoadAutomation())
|
||||
..add(FetchDevicesInRoutine()),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: ListView(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
height: MediaQuery.sizeOf(context).height,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Create New Routines",
|
||||
style:
|
||||
Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
RoutineViewCard(
|
||||
isLoading: false,
|
||||
onChanged: (v) {},
|
||||
status: '',
|
||||
spaceId: '',
|
||||
automationId: '',
|
||||
communityId: '',
|
||||
sceneId: '',
|
||||
cardType: '',
|
||||
spaceName: '',
|
||||
onTap: () => _handleRoutineCreation(context),
|
||||
icon: Icons.add,
|
||||
textString: '',
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Expanded(child: FetchRoutineScenesAutomation()),
|
||||
],
|
||||
),
|
||||
child: SizedBox(
|
||||
height: context.screenHeight,
|
||||
width: context.screenWidth,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsetsDirectional.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 16,
|
||||
children: [
|
||||
Text(
|
||||
"Create New Routines",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
RoutineViewCard(
|
||||
isLoading: false,
|
||||
onChanged: (v) {},
|
||||
status: '',
|
||||
spaceId: '',
|
||||
automationId: '',
|
||||
communityId: '',
|
||||
sceneId: '',
|
||||
cardType: '',
|
||||
spaceName: '',
|
||||
onTap: () => _handleRoutineCreation(context),
|
||||
icon: Icons.add,
|
||||
textString: '',
|
||||
),
|
||||
const FetchRoutineScenesAutomation(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 50),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
33
lib/pages/routines/widgets/condition_toggle.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConditionToggle extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final void Function(String condition) onChanged;
|
||||
|
||||
const ConditionToggle({
|
||||
required this.onChanged,
|
||||
this.currentCondition,
|
||||
super.key,
|
||||
});
|
||||
|
||||
static const _conditions = ["<", "==", ">"];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ToggleButtons(
|
||||
onPressed: (index) => onChanged(_conditions[index]),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
selectedColor: Colors.white,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected: _conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: _conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
@ -8,12 +8,12 @@ class DialogFooter extends StatelessWidget {
|
||||
final int? dialogWidth;
|
||||
|
||||
const DialogFooter({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.onCancel,
|
||||
required this.onConfirm,
|
||||
required this.isConfirmEnabled,
|
||||
this.dialogWidth,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -28,46 +28,52 @@ class DialogFooter extends StatelessWidget {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: _buildFooterButton(
|
||||
context,
|
||||
'Cancel',
|
||||
onCancel,
|
||||
),
|
||||
DialogFooterButton(
|
||||
text: 'Cancel',
|
||||
onTap: onCancel,
|
||||
),
|
||||
if (isConfirmEnabled) ...[
|
||||
Container(width: 1, height: 50, color: ColorsManager.greyColor),
|
||||
Expanded(
|
||||
child: _buildFooterButton(
|
||||
context,
|
||||
'Confirm',
|
||||
onConfirm,
|
||||
),
|
||||
DialogFooterButton(
|
||||
text: 'Confirm',
|
||||
onTap: onConfirm,
|
||||
textColor: isConfirmEnabled
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
: Colors.red,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildFooterButton(
|
||||
BuildContext context,
|
||||
String text,
|
||||
VoidCallback? onTap,
|
||||
) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: SizedBox(
|
||||
height: 50,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: text == 'Confirm'
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
class DialogFooterButton extends StatelessWidget {
|
||||
const DialogFooterButton({
|
||||
required this.text,
|
||||
required this.onTap,
|
||||
this.textColor,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String text;
|
||||
final VoidCallback? onTap;
|
||||
final Color? textColor;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Expanded(
|
||||
child: TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: ColorsManager.primaryColorWithOpacity,
|
||||
disabledForegroundColor: ColorsManager.primaryColor,
|
||||
),
|
||||
onPressed: onTap,
|
||||
child: Text(
|
||||
text,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: textColor ?? ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -16,6 +16,7 @@ class DialogHeader extends StatelessWidget {
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
fontWeight: FontWeight.bold,
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class DraggableCard extends StatelessWidget {
|
||||
@ -68,9 +69,9 @@ class DraggableCard extends StatelessWidget {
|
||||
Card(
|
||||
color: ColorsManager.whiteColors,
|
||||
child: Container(
|
||||
padding: padding ?? const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(16),
|
||||
width: 110,
|
||||
height: deviceFunctions.isEmpty ? 123 : null,
|
||||
height: deviceFunctions.isEmpty ? 160 : 180,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -78,6 +79,7 @@ class DraggableCard extends StatelessWidget {
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
@ -112,8 +114,69 @@ class DraggableCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Visibility(
|
||||
visible: deviceData['tag'] != null && deviceData['tag'] != '',
|
||||
child: Row(
|
||||
spacing: 2,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 8, height: 8, child: SvgPicture.asset(Assets.deviceTagIcon)),
|
||||
Flexible(
|
||||
child: Text(
|
||||
deviceData['tag'] ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
|
||||
child: const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: deviceData['subSpace'] != null && deviceData['subSpace'] != '',
|
||||
child: Row(
|
||||
spacing: 2,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 8,
|
||||
height: 8,
|
||||
child: SvgPicture.asset(Assets.spaceLocationIcon)),
|
||||
Flexible(
|
||||
child: Text(
|
||||
deviceData['subSpace'] ?? '',
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.lightGreyColor,
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (deviceFunctions.isNotEmpty)
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
if (deviceFunctions.isNotEmpty)
|
||||
...deviceFunctions.map((function) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -123,7 +186,7 @@ class DraggableCard extends StatelessWidget {
|
||||
'${function.operationName}: ${_formatFunctionValue(function)}',
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
fontSize: 9,
|
||||
color: ColorsManager.textGray,
|
||||
color: ColorsManager.lightGreyColor,
|
||||
height: 1.2,
|
||||
),
|
||||
maxLines: 2,
|
||||
@ -162,7 +225,7 @@ class DraggableCard extends StatelessWidget {
|
||||
if (function.functionCode == 'temp_set' || function.functionCode == 'temp_current') {
|
||||
return '${(function.value / 10).toStringAsFixed(0)}°C';
|
||||
} else if (function.functionCode.contains('countdown')) {
|
||||
final seconds = function.value.toInt();
|
||||
final seconds = function.value?.toInt() ?? 0;
|
||||
if (seconds >= 3600) {
|
||||
final hours = (seconds / 3600).floor();
|
||||
final remainingMinutes = ((seconds % 3600) / 60).floor();
|
||||
|
35
lib/pages/routines/widgets/function_slider.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FunctionSlider extends StatelessWidget {
|
||||
final dynamic initialValue;
|
||||
final (double min, double max) range;
|
||||
final void Function(double value) onChanged;
|
||||
final double dividendOfRange;
|
||||
|
||||
const FunctionSlider({
|
||||
required this.onChanged,
|
||||
required this.initialValue,
|
||||
required this.range,
|
||||
required this.dividendOfRange,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (min, max) = range;
|
||||
final bool isValidRange = max > min;
|
||||
final double value = initialValue is int
|
||||
? (initialValue as int).toDouble()
|
||||
: (initialValue as double);
|
||||
|
||||
final int? divisions = isValidRange ? ((max - min) / dividendOfRange).round() : null;
|
||||
|
||||
return Slider(
|
||||
value: value.clamp(min, max),
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: divisions,
|
||||
onChanged: isValidRange ? onChanged : null,
|
||||
);
|
||||
}
|
||||
}
|
@ -17,91 +17,87 @@ class IfContainer extends StatelessWidget {
|
||||
builder: (context, state) {
|
||||
return DragTarget<Map<String, dynamic>>(
|
||||
builder: (context, candidateData, rejectedData) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('IF',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
if (state.isAutomation && state.ifItems.isNotEmpty)
|
||||
AutomationOperatorSelector(
|
||||
selectedOperator: state.selectedAutomationOperator),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (state.isTabToRun)
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
DraggableCard(
|
||||
imagePath: Assets.tabToRun,
|
||||
title: 'Tab to run',
|
||||
deviceData: {},
|
||||
),
|
||||
const Text('IF',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
if (state.isAutomation && state.ifItems.isNotEmpty)
|
||||
AutomationOperatorSelector(
|
||||
selectedOperator: state.selectedAutomationOperator),
|
||||
],
|
||||
),
|
||||
if (!state.isTabToRun)
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: List.generate(
|
||||
state.ifItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (!state.isTabToRun) {
|
||||
final result = await DeviceDialogHelper
|
||||
.showDeviceDialog(
|
||||
context: context,
|
||||
data: state.ifItems[index],
|
||||
removeComparetors: false,
|
||||
dialogType: "IF");
|
||||
const SizedBox(height: 16),
|
||||
if (state.isTabToRun)
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
DraggableCard(
|
||||
imagePath: Assets.tabToRun,
|
||||
title: 'Tab to run',
|
||||
deviceData: {},
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!state.isTabToRun)
|
||||
Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: List.generate(
|
||||
state.ifItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (!state.isTabToRun) {
|
||||
final result = await DeviceDialogHelper.showDeviceDialog(
|
||||
context: context,
|
||||
data: state.ifItems[index],
|
||||
removeComparetors: false,
|
||||
dialogType: "IF");
|
||||
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToIfContainer(
|
||||
state.ifItems[index], false));
|
||||
} else if (![
|
||||
'AC',
|
||||
'1G',
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS'
|
||||
'GW',
|
||||
].contains(
|
||||
state.ifItems[index]['productType'])) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToIfContainer(
|
||||
state.ifItems[index], false));
|
||||
if (result != null) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(state.ifItems[index], false));
|
||||
} else if (![
|
||||
'AC',
|
||||
'1G',
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS',
|
||||
'GW',
|
||||
'CPS',
|
||||
].contains(state.ifItems[index]['productType'])) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(state.ifItems[index], false));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
child: DraggableCard(
|
||||
imagePath:
|
||||
state.ifItems[index]['imagePath'] ?? '',
|
||||
title: state.ifItems[index]['title'] ?? '',
|
||||
deviceData: state.ifItems[index],
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 8),
|
||||
isFromThen: false,
|
||||
isFromIf: true,
|
||||
onRemove: () {
|
||||
context.read<RoutineBloc>().add(
|
||||
RemoveDragCard(
|
||||
index: index,
|
||||
isFromThen: false,
|
||||
key: state.ifItems[index]
|
||||
['uniqueCustomId']));
|
||||
},
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
child: DraggableCard(
|
||||
imagePath: state.ifItems[index]['imagePath'] ?? '',
|
||||
title: state.ifItems[index]['title'] ?? '',
|
||||
deviceData: state.ifItems[index],
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
|
||||
isFromThen: false,
|
||||
isFromIf: true,
|
||||
onRemove: () {
|
||||
context.read<RoutineBloc>().add(RemoveDragCard(
|
||||
index: index,
|
||||
isFromThen: false,
|
||||
key: state.ifItems[index]['uniqueCustomId']));
|
||||
},
|
||||
),
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -116,9 +112,7 @@ class IfContainer extends StatelessWidget {
|
||||
|
||||
if (!state.isTabToRun) {
|
||||
if (mutableData['deviceId'] == 'tab_to_run') {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(mutableData, true));
|
||||
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, true));
|
||||
} else {
|
||||
final result = await DeviceDialogHelper.showDeviceDialog(
|
||||
dialogType: 'IF',
|
||||
@ -127,14 +121,10 @@ class IfContainer extends StatelessWidget {
|
||||
removeComparetors: false);
|
||||
|
||||
if (result != null) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(mutableData, false));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
|
||||
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
||||
.contains(mutableData['productType'])) {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToIfContainer(mutableData, false));
|
||||
context.read<RoutineBloc>().add(AddToIfContainer(mutableData, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -180,9 +170,7 @@ class AutomationOperatorSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(const ChangeAutomationOperator(operator: 'or'));
|
||||
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'or'));
|
||||
},
|
||||
),
|
||||
Container(
|
||||
@ -208,9 +196,7 @@ class AutomationOperatorSelector extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<RoutineBloc>()
|
||||
.add(const ChangeAutomationOperator(operator: 'and'));
|
||||
context.read<RoutineBloc>().add(const ChangeAutomationOperator(operator: 'and'));
|
||||
},
|
||||
),
|
||||
],
|
||||
|
@ -8,211 +8,182 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class FetchRoutineScenesAutomation extends StatefulWidget {
|
||||
const FetchRoutineScenesAutomation({super.key});
|
||||
|
||||
@override
|
||||
State<FetchRoutineScenesAutomation> createState() =>
|
||||
_FetchRoutineScenesState();
|
||||
}
|
||||
|
||||
class _FetchRoutineScenesState extends State<FetchRoutineScenesAutomation>
|
||||
class FetchRoutineScenesAutomation extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
const FetchRoutineScenesAutomation({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<RoutineBloc, RoutineState>(
|
||||
builder: (context, state) {
|
||||
return state.isLoading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Scenes (Tab to Run)",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if (state.scenes.isEmpty)
|
||||
Text(
|
||||
"No scenes found",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
if (state.scenes.isNotEmpty)
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.scenes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final scene = state.scenes[index];
|
||||
final isLoading =
|
||||
state.loadingSceneId == scene.id;
|
||||
if (state.isLoading) return const Center(child: CircularProgressIndicator());
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right:
|
||||
isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
RoutineViewCard(
|
||||
isLoading: isLoading,
|
||||
sceneOnTap: () {
|
||||
context.read<RoutineBloc>().add(
|
||||
SceneTrigger(
|
||||
sceneId: scene.id,
|
||||
name: scene.name));
|
||||
},
|
||||
status: state.scenes[index].status,
|
||||
communityId:
|
||||
state.scenes[index].communityId ??
|
||||
'',
|
||||
spaceId: state.scenes[index].spaceId,
|
||||
sceneId:
|
||||
state.scenes[index].sceneTuyaId!,
|
||||
automationId: state.scenes[index].id,
|
||||
cardType: 'scenes',
|
||||
spaceName:
|
||||
state.scenes[index].spaceName,
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context)
|
||||
.add(
|
||||
const CreateNewRoutineViewEvent(
|
||||
createRoutineView: true),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetSceneDetails(
|
||||
sceneId:
|
||||
state.scenes[index].id,
|
||||
isTabToRun: true,
|
||||
isUpdate: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
textString: state.scenes[index].name,
|
||||
icon: state.scenes[index].icon ??
|
||||
Assets.logoHorizontal,
|
||||
isFromScenes: true,
|
||||
iconInBytes:
|
||||
state.scenes[index].iconInBytes,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Automations",
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
if (state.automations.isEmpty)
|
||||
Text(
|
||||
"No automations found",
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
if (state.automations.isNotEmpty)
|
||||
SizedBox(
|
||||
height: 200,
|
||||
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.automations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final isLoading = state.automations!
|
||||
.contains(state.automations[index].id);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: isSmallScreenSize(context)
|
||||
? 4.0
|
||||
: 8.0,
|
||||
),
|
||||
child: RoutineViewCard(
|
||||
isLoading: isLoading,
|
||||
onChanged: (v) {
|
||||
context.read<RoutineBloc>().add(
|
||||
UpdateAutomationStatus(
|
||||
automationId: state
|
||||
.automations[index].id,
|
||||
automationStatusUpdate:
|
||||
AutomationStatusUpdate(
|
||||
spaceUuid: state
|
||||
.automations[
|
||||
index]
|
||||
.spaceId,
|
||||
isEnable: v),
|
||||
communityId: state
|
||||
.automations[index]
|
||||
.communityId,
|
||||
),
|
||||
);
|
||||
},
|
||||
status: state.automations[index].status,
|
||||
communityId: '',
|
||||
spaceId:
|
||||
state.automations[index].spaceId,
|
||||
sceneId: '',
|
||||
automationId:
|
||||
state.automations[index].id,
|
||||
cardType: 'automations',
|
||||
spaceName:
|
||||
state.automations[index].spaceName,
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context)
|
||||
.add(
|
||||
const CreateNewRoutineViewEvent(
|
||||
createRoutineView: true),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetAutomationDetails(
|
||||
automationId: state
|
||||
.automations[index].id,
|
||||
isAutomation: true,
|
||||
isUpdate: true),
|
||||
);
|
||||
},
|
||||
textString:
|
||||
state.automations[index].name,
|
||||
icon: state.automations[index].icon ??
|
||||
Assets.automation,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildListTitle(context, "Scenes (Tab to Run)"),
|
||||
const SizedBox(height: 10),
|
||||
Visibility(
|
||||
visible: state.scenes.isNotEmpty,
|
||||
replacement: _buildEmptyState(context, "No scenes found"),
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _buildScenes(state),
|
||||
),
|
||||
),
|
||||
);
|
||||
const SizedBox(height: 10),
|
||||
_buildListTitle(context, "Automations"),
|
||||
const SizedBox(height: 3),
|
||||
Visibility(
|
||||
visible: state.automations.isNotEmpty,
|
||||
replacement: _buildEmptyState(context, "No automations found"),
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: _buildAutomations(state),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAutomations(RoutineState state) {
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.automations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final isLoading = state.automations.contains(state.automations[index].id);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||
),
|
||||
child: RoutineViewCard(
|
||||
isLoading: isLoading,
|
||||
onChanged: (v) {
|
||||
context.read<RoutineBloc>().add(
|
||||
UpdateAutomationStatus(
|
||||
automationId: state.automations[index].id,
|
||||
automationStatusUpdate: AutomationStatusUpdate(
|
||||
spaceUuid: state.automations[index].spaceId,
|
||||
isEnable: v,
|
||||
),
|
||||
communityId: state.automations[index].communityId,
|
||||
),
|
||||
);
|
||||
},
|
||||
status: state.automations[index].status,
|
||||
communityId: '',
|
||||
spaceId: state.automations[index].spaceId,
|
||||
sceneId: '',
|
||||
automationId: state.automations[index].id,
|
||||
cardType: 'automations',
|
||||
spaceName: state.automations[index].spaceName,
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(
|
||||
createRoutineView: true,
|
||||
),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetAutomationDetails(
|
||||
automationId: state.automations[index].id,
|
||||
isAutomation: true,
|
||||
isUpdate: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
textString: state.automations[index].name,
|
||||
icon: state.automations[index].icon ?? Assets.automation,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildScenes(RoutineState state) {
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.scenes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final scene = state.scenes[index];
|
||||
final isLoading = state.loadingSceneId == scene.id;
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
right: isSmallScreenSize(context) ? 4.0 : 8.0,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
RoutineViewCard(
|
||||
isLoading: isLoading,
|
||||
sceneOnTap: () {
|
||||
context.read<RoutineBloc>().add(
|
||||
SceneTrigger(
|
||||
sceneId: scene.id,
|
||||
name: scene.name,
|
||||
),
|
||||
);
|
||||
},
|
||||
status: state.scenes[index].status,
|
||||
communityId: state.scenes[index].communityId,
|
||||
spaceId: state.scenes[index].spaceId,
|
||||
sceneId: state.scenes[index].sceneTuyaId!,
|
||||
automationId: state.scenes[index].id,
|
||||
cardType: 'scenes',
|
||||
spaceName: state.scenes[index].spaceName,
|
||||
onTap: () {
|
||||
BlocProvider.of<RoutineBloc>(context).add(
|
||||
const CreateNewRoutineViewEvent(
|
||||
createRoutineView: true,
|
||||
),
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
GetSceneDetails(
|
||||
sceneId: state.scenes[index].id,
|
||||
isTabToRun: true,
|
||||
isUpdate: true,
|
||||
),
|
||||
);
|
||||
},
|
||||
textString: state.scenes[index].name,
|
||||
icon: state.scenes[index].icon ?? Assets.logoHorizontal,
|
||||
isFromScenes: true,
|
||||
iconInBytes: state.scenes[index].iconInBytes,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildListTitle(BuildContext context, String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(BuildContext context, String title) {
|
||||
return Text(
|
||||
title,
|
||||
style: context.textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -66,7 +67,6 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Use widget.<mixinMethod> instead of just <mixinMethod>
|
||||
final double cardWidth = widget.isSmallScreenSize(context)
|
||||
? 120
|
||||
: widget.isMediumScreenSize(context)
|
||||
@ -121,29 +121,29 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
||||
child: SizedBox(
|
||||
width: 16,
|
||||
height: 16,
|
||||
child:
|
||||
CircularProgressIndicator(strokeWidth: 2),
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
CupertinoSwitch(
|
||||
activeColor: ColorsManager.primaryColor,
|
||||
activeTrackColor: ColorsManager.primaryColor,
|
||||
value: widget.status == 'enable',
|
||||
onChanged: widget.onChanged,
|
||||
)
|
||||
],
|
||||
)
|
||||
: const SizedBox(),
|
||||
InkWell(
|
||||
onTap: widget.onTap,
|
||||
child: Column(
|
||||
children: [
|
||||
Center(
|
||||
Column(
|
||||
children: [
|
||||
Center(
|
||||
child: InkWell(
|
||||
customBorder: const CircleBorder(),
|
||||
onTap: widget.onTap,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.graysColor,
|
||||
borderRadius: BorderRadius.circular(120),
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager.greyColor,
|
||||
width: 2.0,
|
||||
@ -159,9 +159,8 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
||||
height: iconSize,
|
||||
width: iconSize,
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder:
|
||||
(context, error, stackTrace) =>
|
||||
Image.asset(
|
||||
errorBuilder: (context, error, stackTrace) =>
|
||||
Image.asset(
|
||||
Assets.logo,
|
||||
height: iconSize,
|
||||
width: iconSize,
|
||||
@ -191,52 +190,48 @@ class _RoutineViewCardState extends State<RoutineViewCard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
widget.textString,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize:
|
||||
widget.isSmallScreenSize(context) ? 10 : 12,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 3),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
widget.textString,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize: widget.isSmallScreenSize(context) ? 10 : 12,
|
||||
),
|
||||
if (widget.spaceName != '')
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.spaceLocationIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
if (widget.spaceName != '')
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
Assets.spaceLocationIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
Text(
|
||||
widget.spaceName,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize:
|
||||
widget.isSmallScreenSize(context) ? 10 : 12,
|
||||
),
|
||||
Text(
|
||||
widget.spaceName,
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
style:
|
||||
context.textTheme.bodySmall?.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
fontSize:
|
||||
widget.isSmallScreenSize(context)
|
||||
? 10
|
||||
: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -17,7 +17,7 @@ class _RoutineDevicesState extends State<RoutineDevices> {
|
||||
context.read<RoutineBloc>().add(FetchDevicesInRoutine());
|
||||
}
|
||||
|
||||
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW'};
|
||||
static const _allowedProductTypes = {'AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS'};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -51,12 +51,12 @@ class _RoutineDevicesState extends State<RoutineDevices> {
|
||||
'productType': device.productType,
|
||||
'functions': device.functions,
|
||||
'uniqueCustomId': '',
|
||||
'tag': device.deviceTags!.isNotEmpty ? device.deviceTags![0].name : '',
|
||||
'subSpace': device.deviceSubSpace?.subspaceName ?? '',
|
||||
};
|
||||
|
||||
if (state.searchText != null && state.searchText!.isNotEmpty) {
|
||||
return device.name!
|
||||
.toLowerCase()
|
||||
.contains(state.searchText!.toLowerCase())
|
||||
return device.name!.toLowerCase().contains(state.searchText!.toLowerCase())
|
||||
? DraggableCard(
|
||||
imagePath: deviceData['imagePath'] as String,
|
||||
title: deviceData['title'] as String,
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_operational_value.dart';
|
||||
@ -9,8 +11,6 @@ import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
|
||||
class ACHelper {
|
||||
static Future<Map<String, dynamic>?> showACFunctionsDialog({
|
||||
@ -74,10 +74,8 @@ class ACHelper {
|
||||
child: _buildFunctionsList(
|
||||
context: context,
|
||||
acFunctions: acFunctions,
|
||||
onFunctionSelected:
|
||||
(functionCode, operationName) => context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
onFunctionSelected: (functionCode, operationName) =>
|
||||
context.read<FunctionBloc>().add(SelectFunction(
|
||||
functionCode: functionCode,
|
||||
operationName: operationName,
|
||||
)),
|
||||
@ -191,8 +189,9 @@ class ACHelper {
|
||||
required String operationName,
|
||||
bool? removeComparators,
|
||||
}) {
|
||||
final initialVal = selectedFunction == 'temp_set' ? 200 : -100;
|
||||
if (selectedFunction == 'temp_set' || selectedFunction == 'temp_current') {
|
||||
final initialValue = selectedFunctionData?.value ?? 250;
|
||||
final initialValue = selectedFunctionData?.value ?? initialVal;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
@ -205,8 +204,7 @@ class ACHelper {
|
||||
);
|
||||
}
|
||||
|
||||
final selectedFn =
|
||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final selectedFn = acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
return _buildOperationalValuesList(
|
||||
@ -287,7 +285,9 @@ class ACHelper {
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value,
|
||||
value: selectedFunctionData?.value ?? selectCode == 'temp_set'
|
||||
? 200
|
||||
: -100,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
@ -302,8 +302,7 @@ class ACHelper {
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
@ -317,6 +316,7 @@ class ACHelper {
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
final initialVal = selectCode == 'temp_set' ? 200 : -100;
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
@ -324,7 +324,7 @@ class ACHelper {
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'${(initialValue ?? 200) / 10}°C',
|
||||
'${(initialValue ?? initialVal) / 10}°C',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
@ -397,9 +397,7 @@ class ACHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
@ -415,8 +413,7 @@ class ACHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,219 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_slider_selector.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_dialog_value_selector.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_functions_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||
|
||||
class CeilingSensorDialog extends StatefulWidget {
|
||||
const CeilingSensorDialog({
|
||||
required this.uniqueCustomId,
|
||||
required this.functions,
|
||||
required this.deviceSelectedFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String? uniqueCustomId;
|
||||
final List<DeviceFunction> functions;
|
||||
final List<DeviceFunctionData> deviceSelectedFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
@override
|
||||
State<CeilingSensorDialog> createState() => _CeilingSensorDialogState();
|
||||
}
|
||||
|
||||
class _CeilingSensorDialogState extends State<CeilingSensorDialog> {
|
||||
late final List<CpsFunctions> _cpsFunctions;
|
||||
late final String _dialogHeaderText;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_cpsFunctions = widget.functions.whereType<CpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
return function.type == 'IF' || function.type == 'BOTH';
|
||||
}).toList();
|
||||
|
||||
final isIfDialog = widget.dialogType == 'IF';
|
||||
_dialogHeaderText = isIfDialog ? 'Conditions' : 'Functions';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||
builder: (context, state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
|
||||
return Container(
|
||||
width: selectedFunction != null ? 600 : 360,
|
||||
height: 450,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
DialogHeader('Presence Sensor $_dialogHeaderText'),
|
||||
Expanded(child: _buildMainContent(context, state)),
|
||||
DialogFooter(
|
||||
onCancel: () => Navigator.pop(context),
|
||||
onConfirm: state.addedFunctions.isNotEmpty
|
||||
? () {
|
||||
final functions = _updateValuesForAddedFunctions(
|
||||
state.addedFunctions,
|
||||
);
|
||||
context.read<RoutineBloc>().add(
|
||||
AddFunctionToRoutine(
|
||||
functions,
|
||||
'${widget.uniqueCustomId}',
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.pop(context, {
|
||||
'deviceId': widget.functions.first.deviceId,
|
||||
});
|
||||
}
|
||||
: null,
|
||||
isConfirmEnabled: selectedFunction != null,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainContent(BuildContext context, FunctionBlocState state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
final selectedOperationName = state.selectedOperationName;
|
||||
final selectedFunctionData = state.addedFunctions.firstWhere(
|
||||
(f) => f.functionCode == selectedFunction,
|
||||
orElse: () => DeviceFunctionData(
|
||||
entityId: '',
|
||||
functionCode: selectedFunction ?? '',
|
||||
operationName: '',
|
||||
value: null,
|
||||
),
|
||||
);
|
||||
final selectedCpsFunctions = _cpsFunctions.firstWhere(
|
||||
(f) => f.code == selectedFunction,
|
||||
orElse: () => CpsMovementFunctions(
|
||||
deviceId: '',
|
||||
deviceName: '',
|
||||
type: '',
|
||||
),
|
||||
);
|
||||
final operations = selectedCpsFunctions.getOperationalValues();
|
||||
final isSensitivityFunction = selectedFunction == 'sensitivity';
|
||||
final isToggleFunction = isSensitivityFunction
|
||||
? widget.dialogType == 'THEN'
|
||||
: CeilingSensorHelper.toggleCodes.contains(selectedFunction);
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
CpsFunctionsList(cpsFunctions: _cpsFunctions),
|
||||
if (state.selectedFunction != null)
|
||||
Expanded(
|
||||
child: isToggleFunction
|
||||
? CpsDialogValueSelector(
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
cpsFunctions: _cpsFunctions,
|
||||
operationName: selectedOperationName ?? '',
|
||||
device: widget.device,
|
||||
)
|
||||
: CpsDialogSliderSelector(
|
||||
operations: operations,
|
||||
selectedFunction: selectedFunction ?? '',
|
||||
selectedFunctionData: selectedFunctionData,
|
||||
cpsFunctions: _cpsFunctions,
|
||||
operationName: selectedOperationName ?? '',
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static const _mappableSteppedFunctions = <String>{
|
||||
'static_max_dis',
|
||||
'presence_reference',
|
||||
'moving_reference',
|
||||
'perceptual_boundary',
|
||||
'moving_boundary',
|
||||
'moving_rigger_time',
|
||||
'moving_static_time',
|
||||
'none_body_time',
|
||||
'moving_max_dis',
|
||||
'moving_range',
|
||||
'presence_range',
|
||||
};
|
||||
|
||||
List<DeviceFunctionData> _updateValuesForAddedFunctions(
|
||||
List<DeviceFunctionData> addedFunctions,
|
||||
) {
|
||||
return addedFunctions.map((function) {
|
||||
final shouldMapValue = _mappableSteppedFunctions.contains(
|
||||
function.functionCode,
|
||||
);
|
||||
if (shouldMapValue) {
|
||||
final mappedValue = _mapSteppedValue(
|
||||
value: function.value,
|
||||
inputStep: CpsSliderHelpers.dividendOfRange(function.functionCode),
|
||||
inputRange: CpsSliderHelpers.sliderRange(function.functionCode),
|
||||
outputRange: CpsSliderHelpers.mappedRange(function.functionCode),
|
||||
);
|
||||
return DeviceFunctionData(
|
||||
value: mappedValue,
|
||||
entityId: function.entityId,
|
||||
functionCode: function.functionCode,
|
||||
operationName: function.operationName,
|
||||
condition: function.condition,
|
||||
actionExecutor: function.actionExecutor,
|
||||
valueDescription: function.valueDescription,
|
||||
);
|
||||
}
|
||||
return function;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
int _mapSteppedValue({
|
||||
required (double min, double max) inputRange,
|
||||
required double inputStep,
|
||||
required (double min, double max, double dividend) outputRange,
|
||||
required double value,
|
||||
}) {
|
||||
final (inputMin, inputMax) = inputRange;
|
||||
final (outputMin, outputMax, outputStep) = outputRange;
|
||||
|
||||
final clampedValue = value.clamp(inputMin, inputMax);
|
||||
|
||||
final stepsFromMin = ((clampedValue - inputMin) / inputStep).round();
|
||||
|
||||
final mappedValue = outputMin + (stepsFromMin * outputStep);
|
||||
|
||||
return mappedValue.clamp(outputMin, outputMax).round();
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/ceiling_sensor_dialog.dart';
|
||||
|
||||
abstract final class CeilingSensorHelper {
|
||||
const CeilingSensorHelper._();
|
||||
|
||||
static Future<Map<String, dynamic>?> showCeilingSensorDialog({
|
||||
required BuildContext context,
|
||||
required String? uniqueCustomId,
|
||||
required List<DeviceFunction> functions,
|
||||
required List<DeviceFunctionData> deviceSelectedFunctions,
|
||||
required AllDevicesModel? device,
|
||||
required String dialogType,
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider(
|
||||
create: (context) => FunctionBloc()
|
||||
..add(
|
||||
InitializeFunctions(deviceSelectedFunctions),
|
||||
),
|
||||
child: CeilingSensorDialog(
|
||||
uniqueCustomId: uniqueCustomId,
|
||||
functions: functions,
|
||||
deviceSelectedFunctions: deviceSelectedFunctions,
|
||||
device: device,
|
||||
dialogType: dialogType,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static List<DeviceFunction<CpsOperationalValue>> getCeilingSensorFunctions({
|
||||
required String uuid,
|
||||
required String name,
|
||||
}) {
|
||||
return [
|
||||
CpsRadarSwitchFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSpatialParameterSwitchFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSensitivityFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMovingSpeedFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSpatialStaticValueFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSpatialMotionValueFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsMaxDistanceOfDetectionFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMaxDistanceOfStaticDetectionFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsDetectionRangeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsDistanceOfMovingObjectsFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsPresenceJudgementThrsholdFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionAmplitudeTriggerThresholdFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsPerpetualBoundaryFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionTriggerBoundaryFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionTriggerTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMotionToStaticTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsEnteringNoBodyStateTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSelfTestResultFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsNobodyTimeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsMovementFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsCustomModeFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsSpaceTypeFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'BOTH',
|
||||
),
|
||||
CpsPresenceStatusFunctions(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
CpsSportsParaFunction(
|
||||
deviceName: name,
|
||||
deviceId: uuid,
|
||||
type: 'IF',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
static const toggleCodes = <String>{
|
||||
'radar_switch',
|
||||
'space_para_switch',
|
||||
'checking_result',
|
||||
'nobody_time',
|
||||
'body_movement',
|
||||
'custom_mode',
|
||||
'scene',
|
||||
'presence_state',
|
||||
};
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/ceiling_sensor/cps_slider_helpers.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
class CpsDialogSliderSelector extends StatelessWidget {
|
||||
const CpsDialogSliderSelector({
|
||||
required this.operations,
|
||||
required this.selectedFunction,
|
||||
required this.selectedFunctionData,
|
||||
required this.cpsFunctions,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.dialogType,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<CpsOperationalValue> operations;
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData selectedFunctionData;
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String dialogType;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliderValueSelector(
|
||||
currentCondition: selectedFunctionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: CpsSliderHelpers.sliderRange(selectedFunctionData.functionCode),
|
||||
displayedValue: CpsSliderHelpers.displayText(
|
||||
value: selectedFunctionData.value,
|
||||
functionCode: selectedFunctionData.functionCode,
|
||||
),
|
||||
initialValue: selectedFunctionData.value ?? 0,
|
||||
unit: CpsSliderHelpers.unit(selectedFunctionData.functionCode),
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
condition: condition,
|
||||
value: selectedFunctionData.value ?? 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
value: double.parse(value.toStringAsFixed(2)),
|
||||
condition: selectedFunctionData.condition,
|
||||
),
|
||||
),
|
||||
),
|
||||
dividendOfRange: CpsSliderHelpers.dividendOfRange(
|
||||
selectedFunctionData.functionCode,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_selection_list_tile.dart';
|
||||
|
||||
class CpsDialogValueSelector extends StatelessWidget {
|
||||
const CpsDialogValueSelector({
|
||||
required this.operations,
|
||||
required this.selectedFunction,
|
||||
required this.selectedFunctionData,
|
||||
required this.cpsFunctions,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final List<CpsOperationalValue> operations;
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData? selectedFunctionData;
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
itemCount: operations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final operation = operations[index];
|
||||
final isSelected = selectedFunctionData?.value == operation.value;
|
||||
return RoutineDialogSelectionListTile(
|
||||
iconPath: operation.icon,
|
||||
description: operation.description,
|
||||
isSelected: isSelected,
|
||||
onTap: () {
|
||||
if (!isSelected) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: operationName,
|
||||
value: operation.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ceiling_presence_sensor_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialog_function_list_tile.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CpsFunctionsList extends StatelessWidget {
|
||||
const CpsFunctionsList({required this.cpsFunctions, super.key});
|
||||
|
||||
final List<CpsFunctions> cpsFunctions;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 360,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: false,
|
||||
itemCount: cpsFunctions.length,
|
||||
separatorBuilder: (context, index) => const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 40.0),
|
||||
child: Divider(color: ColorsManager.dividerColor),
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final function = cpsFunctions[index];
|
||||
return RoutineDialogFunctionListTile(
|
||||
iconPath: function.icon,
|
||||
operationName: function.operationName,
|
||||
onTap: () => context.read<FunctionBloc>().add(
|
||||
SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName: function.operationName,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
abstract final class CpsSliderHelpers {
|
||||
static (double min, double max, double step) mappedRange(String functionCode) {
|
||||
final (defaultMin, defaultMax) = sliderRange(functionCode);
|
||||
final defaultDivdidend = dividendOfRange(functionCode);
|
||||
return switch (functionCode) {
|
||||
'static_max_dis' => (0, 500, 50),
|
||||
'presence_reference' => (0, 255, 5),
|
||||
'moving_reference' => (0, 255, 5),
|
||||
'perceptual_boundary' => (0, 500, 50),
|
||||
'moving_boundary' => (0, 500, 50),
|
||||
'moving_rigger_time' => (0, 2000, 100),
|
||||
'moving_static_time' => (0, 60000, 1000),
|
||||
'none_body_time' => (0, 300000, 5000),
|
||||
'moving_max_dis' => (0, 500, 50),
|
||||
'moving_range' => (0, 255, 5),
|
||||
'presence_range' => (0, 255, 5),
|
||||
_ => (defaultMin, defaultMax, defaultDivdidend),
|
||||
};
|
||||
}
|
||||
|
||||
static (double min, double max) sliderRange(String functionCode) =>
|
||||
switch (functionCode) {
|
||||
'moving_speed' => (0, 32),
|
||||
'sensitivity' => (0, 10),
|
||||
'space_static_val' => (0, 255),
|
||||
'space_move_val' => (0, 255),
|
||||
'moving_max_dis' => (0, 10),
|
||||
'static_max_dis' => (0, 10),
|
||||
'moving_range' => (0, 25.5),
|
||||
'presence_range' => (0, 25.5),
|
||||
'presence_judgement_threshold' => (0, 255),
|
||||
'motion_amplitude_trigger_threshold' => (0, 255),
|
||||
'perceptual_boundary' => (0, 5),
|
||||
'moving_boundary' => (0, 5),
|
||||
'moving_rigger_time' => (0, 2),
|
||||
'moving_static_time' => (0, 50),
|
||||
'none_body_time' => (0, 300),
|
||||
'sports_para' => (0, 100),
|
||||
_ => (0, 300),
|
||||
};
|
||||
|
||||
static double dividendOfRange(String functionCode) => switch (functionCode) {
|
||||
'presence_reference' => 5,
|
||||
'moving_reference' => 5,
|
||||
'moving_max_dis' => 0.5,
|
||||
'static_max_dis' => 0.5,
|
||||
'moving_range' => 0.1,
|
||||
'presence_range' => 0.1,
|
||||
'perceptual_boundary' => 0.5,
|
||||
'moving_boundary' => 0.5,
|
||||
'moving_rigger_time' => 0.1,
|
||||
'moving_static_time' => 1.0,
|
||||
'none_body_time' => 5.0,
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
static String unit(String functionCode) => switch (functionCode) {
|
||||
'moving_max_dis' ||
|
||||
'static_max_dis' ||
|
||||
'moving_range' ||
|
||||
'presence_range' ||
|
||||
'perceptual_boundary' ||
|
||||
'moving_boundary' =>
|
||||
'M',
|
||||
'moving_rigger_time' || 'moving_static_time' || 'none_body_time' => 'sec',
|
||||
_ => '',
|
||||
};
|
||||
|
||||
static String displayText({
|
||||
required dynamic value,
|
||||
required String functionCode,
|
||||
}) {
|
||||
final parsedValue = double.tryParse('$value');
|
||||
|
||||
return switch (functionCode) {
|
||||
'moving_max_dis' ||
|
||||
'static_max_dis' ||
|
||||
'moving_range' ||
|
||||
'presence_range' ||
|
||||
'perceptual_boundary' ||
|
||||
'moving_boundary' =>
|
||||
parsedValue?.toStringAsFixed(1) ?? '0',
|
||||
'moving_rigger_time' => parsedValue?.toStringAsFixed(2) ?? '0',
|
||||
'moving_static_time' ||
|
||||
'none_body_time' =>
|
||||
parsedValue?.toStringAsFixed(2) ?? '0',
|
||||
_ => '${parsedValue?.toStringAsFixed(0) ?? 0}',
|
||||
};
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_mo
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/duration_format_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/ac/ac_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/base_switch_function.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/gang_switches/one_gang_switch/one_gang_switch.dart';
|
||||
@ -25,23 +24,21 @@ class OneGangSwitchHelper {
|
||||
required String uniqueCustomId,
|
||||
required bool removeComparetors,
|
||||
}) async {
|
||||
List<BaseSwitchFunction> oneGangFunctions =
|
||||
functions.whereType<BaseSwitchFunction>().toList();
|
||||
List<BaseSwitchFunction> oneGangFunctions = functions.whereType<BaseSwitchFunction>().toList();
|
||||
|
||||
return showDialog<Map<String, dynamic>?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (_) => FunctionBloc()
|
||||
..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
create: (_) => FunctionBloc()..add(InitializeFunctions(deviceSelectedFunctions ?? [])),
|
||||
child: AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: BlocBuilder<FunctionBloc, FunctionBlocState>(
|
||||
builder: (context, state) {
|
||||
final selectedFunction = state.selectedFunction;
|
||||
final selectedOperationName = state.selectedOperationName;
|
||||
final selectedFunctionData = state.addedFunctions
|
||||
.firstWhere((f) => f.functionCode == selectedFunction,
|
||||
final selectedFunctionData =
|
||||
state.addedFunctions.firstWhere((f) => f.functionCode == selectedFunction,
|
||||
orElse: () => DeviceFunctionData(
|
||||
entityId: '',
|
||||
functionCode: selectedFunction ?? '',
|
||||
@ -88,12 +85,9 @@ class OneGangSwitchHelper {
|
||||
color: ColorsManager.textGray,
|
||||
),
|
||||
onTap: () {
|
||||
context
|
||||
.read<FunctionBloc>()
|
||||
.add(SelectFunction(
|
||||
context.read<FunctionBloc>().add(SelectFunction(
|
||||
functionCode: function.code,
|
||||
operationName:
|
||||
function.operationName,
|
||||
operationName: function.operationName,
|
||||
));
|
||||
},
|
||||
);
|
||||
@ -170,7 +164,7 @@ class OneGangSwitchHelper {
|
||||
required bool removeComparetors,
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1') {
|
||||
final initialValue = selectedFunctionData?.value ?? 200;
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildCountDownSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
@ -226,11 +220,11 @@ class OneGangSwitchHelper {
|
||||
selectedFunctionData,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownDisplay(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
_buildCountDownDisplay(
|
||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
||||
const SizedBox(height: 20),
|
||||
_buildCountDownSlider(context, initialValue, device, operationName,
|
||||
selectedFunctionData, selectCode),
|
||||
_buildCountDownSlider(
|
||||
context, initialValue, device, operationName, selectedFunctionData, selectCode),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -256,7 +250,7 @@ class OneGangSwitchHelper {
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
@ -271,8 +265,7 @@ class OneGangSwitchHelper {
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
isSelected: conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
@ -308,20 +301,20 @@ class OneGangSwitchHelper {
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: 86400,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
value: (initialValue ?? 0).toDouble(),
|
||||
min: operationalValues.minValue?.toDouble() ?? 0.0,
|
||||
max: operationalValues.maxValue?.toDouble() ?? 0.0,
|
||||
divisions: (((operationalValues.maxValue ?? 0) -
|
||||
(operationalValues.minValue ?? 0)) /
|
||||
divisions: (((operationalValues.maxValue ?? 0) - (operationalValues.minValue ?? 0)) /
|
||||
(operationalValues.stepValue ?? 1))
|
||||
.round(),
|
||||
onChanged: (value) {
|
||||
@ -373,13 +366,9 @@ class OneGangSwitchHelper {
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
trailing: Icon(
|
||||
isSelected
|
||||
? Icons.radio_button_checked
|
||||
: Icons.radio_button_unchecked,
|
||||
isSelected ? Icons.radio_button_checked : Icons.radio_button_unchecked,
|
||||
size: 24,
|
||||
color: isSelected
|
||||
? ColorsManager.primaryColorWithOpacity
|
||||
: ColorsManager.textGray,
|
||||
color: isSelected ? ColorsManager.primaryColorWithOpacity : ColorsManager.textGray,
|
||||
),
|
||||
onTap: () {
|
||||
if (!isSelected) {
|
||||
@ -391,8 +380,7 @@ class OneGangSwitchHelper {
|
||||
operationName: operationName,
|
||||
value: value.value,
|
||||
condition: selectedFunctionData?.condition,
|
||||
valueDescription:
|
||||
selectedFunctionData?.valueDescription,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -170,7 +170,7 @@ class ThreeGangSwitchHelper {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2' ||
|
||||
selectedFunction == 'countdown_3') {
|
||||
final initialValue = selectedFunctionData?.value ?? 200;
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
@ -251,7 +251,7 @@ class ThreeGangSwitchHelper {
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
@ -303,12 +303,13 @@ class ThreeGangSwitchHelper {
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: 86400,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
|
@ -169,7 +169,7 @@ class TwoGangSwitchHelper {
|
||||
}) {
|
||||
if (selectedFunction == 'countdown_1' ||
|
||||
selectedFunction == 'countdown_2') {
|
||||
final initialValue = selectedFunctionData?.value ?? 200;
|
||||
final initialValue = selectedFunctionData?.value ?? 0;
|
||||
return _buildTemperatureSelector(
|
||||
context: context,
|
||||
initialValue: initialValue,
|
||||
@ -250,7 +250,7 @@ class TwoGangSwitchHelper {
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: conditions[index],
|
||||
value: selectedFunctionData?.value,
|
||||
value: selectedFunctionData?.value ?? 0,
|
||||
valueDescription: selectedFunctionData?.valueDescription,
|
||||
),
|
||||
),
|
||||
@ -302,12 +302,13 @@ class TwoGangSwitchHelper {
|
||||
DeviceFunctionData? selectedFunctionData,
|
||||
String selectCode,
|
||||
) {
|
||||
const twelveHoursInSeconds = 43200.0;
|
||||
final operationalValues = SwitchOperationalValue(
|
||||
icon: '',
|
||||
description: "sec",
|
||||
value: 0.0,
|
||||
minValue: 0,
|
||||
maxValue: 86400,
|
||||
maxValue: twelveHoursInSeconds,
|
||||
stepValue: 1,
|
||||
);
|
||||
return Slider(
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
@ -28,9 +27,12 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_hoursController = FixedExtentScrollController(initialItem: widget.initialHours);
|
||||
_minutesController = FixedExtentScrollController(initialItem: widget.initialMinutes);
|
||||
_secondsController = FixedExtentScrollController(initialItem: widget.initialSeconds);
|
||||
_hoursController =
|
||||
FixedExtentScrollController(initialItem: widget.initialHours);
|
||||
_minutesController =
|
||||
FixedExtentScrollController(initialItem: widget.initialMinutes);
|
||||
_secondsController =
|
||||
FixedExtentScrollController(initialItem: widget.initialSeconds);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -47,6 +49,8 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_hoursController.dispose();
|
||||
@ -61,26 +65,28 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
_buildPickerColumn(
|
||||
label: 'h',
|
||||
controller: _hoursController,
|
||||
itemCount: 24,
|
||||
onChanged: (value) => _handleTimeChange(
|
||||
value,
|
||||
_minutesController.selectedItem,
|
||||
_secondsController.selectedItem,
|
||||
),
|
||||
),
|
||||
label: 'h',
|
||||
controller: _hoursController,
|
||||
itemCount: 3,
|
||||
onChanged: (value) {
|
||||
_handleTimeChange(
|
||||
value,
|
||||
_minutesController.selectedItem,
|
||||
_secondsController.selectedItem,
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 5),
|
||||
_buildPickerColumn(
|
||||
label: 'm',
|
||||
controller: _minutesController,
|
||||
itemCount: 60,
|
||||
onChanged: (value) => _handleTimeChange(
|
||||
_hoursController.selectedItem,
|
||||
value,
|
||||
_secondsController.selectedItem,
|
||||
),
|
||||
),
|
||||
label: 'm',
|
||||
controller: _minutesController,
|
||||
itemCount: 60,
|
||||
onChanged: (value) {
|
||||
_handleTimeChange(
|
||||
_hoursController.selectedItem,
|
||||
value,
|
||||
_secondsController.selectedItem,
|
||||
);
|
||||
}),
|
||||
const SizedBox(width: 5),
|
||||
_buildPickerColumn(
|
||||
label: 's',
|
||||
@ -97,6 +103,19 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
}
|
||||
|
||||
void _handleTimeChange(int hours, int minutes, int seconds) {
|
||||
int total = hours * 3600 + minutes * 60 + seconds;
|
||||
if (total > 10000) {
|
||||
hours = 2;
|
||||
minutes = 46;
|
||||
seconds = 40;
|
||||
total = 10000;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_hoursController.jumpToItem(hours);
|
||||
_minutesController.jumpToItem(minutes);
|
||||
_secondsController.jumpToItem(seconds);
|
||||
});
|
||||
}
|
||||
|
||||
widget.onTimeChanged(hours, minutes, seconds);
|
||||
}
|
||||
|
||||
@ -147,4 +166,4 @@ class _TimeWheelPickerState extends State<TimeWheelPicker> {
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/routine_bloc/routine_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_footer.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dialog_header.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_value_selector_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
|
||||
class WallPresenceSensor extends StatefulWidget {
|
||||
final List<DeviceFunction> functions;
|
||||
@ -63,8 +62,7 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_wpsFunctions =
|
||||
widget.functions.whereType<WpsFunctions>().where((function) {
|
||||
_wpsFunctions = widget.functions.whereType<WpsFunctions>().where((function) {
|
||||
if (widget.dialogType == 'THEN') {
|
||||
return function.type == 'THEN' || function.type == 'BOTH';
|
||||
}
|
||||
@ -176,10 +174,10 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: _ValueSelector(
|
||||
child: WpsValueSelectorWidget(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
acFunctions: _wpsFunctions,
|
||||
wpsFunctions: _wpsFunctions,
|
||||
device: widget.device,
|
||||
dialogType: widget.dialogType!,
|
||||
removeComparators: widget.removeComparetors,
|
||||
@ -208,342 +206,3 @@ class _WallPresenceSensorState extends State<WallPresenceSensor> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ValueSelector extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WpsFunctions> acFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
final bool removeComparators;
|
||||
|
||||
const _ValueSelector({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.acFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
required this.removeComparators,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn =
|
||||
acFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return _SliderValueSelector(
|
||||
selectedFunction: selectedFunction,
|
||||
functionData: functionData,
|
||||
device: device,
|
||||
dialogType: dialogType,
|
||||
);
|
||||
}
|
||||
|
||||
return _OperationalValuesList(
|
||||
values: values,
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isSliderFunction(String function) =>
|
||||
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
||||
}
|
||||
|
||||
class _SliderValueSelector extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
|
||||
const _SliderValueSelector({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final initialValue = functionData.value ?? 250;
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 20),
|
||||
_ConditionToggle(
|
||||
currentCondition: functionData.condition,
|
||||
selectCode: selectedFunction,
|
||||
device: device,
|
||||
operationName: functionData.operationName,
|
||||
selectedValue: functionData.value,
|
||||
),
|
||||
_ValueDisplay(
|
||||
value: initialValue,
|
||||
functionCode: selectedFunction,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
_FunctionSlider(
|
||||
initialValue: initialValue,
|
||||
functionCode: selectedFunction,
|
||||
functionData: functionData,
|
||||
device: device,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ConditionToggle extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final String selectCode;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final dynamic selectedValue;
|
||||
|
||||
const _ConditionToggle({
|
||||
this.currentCondition,
|
||||
required this.selectCode,
|
||||
this.device,
|
||||
required this.operationName,
|
||||
this.selectedValue,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const conditions = ["<", "==", ">"];
|
||||
return ToggleButtons(
|
||||
onPressed: (index) => _updateCondition(context, conditions[index]),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||
selectedBorderColor: ColorsManager.primaryColorWithOpacity,
|
||||
selectedColor: Colors.white,
|
||||
fillColor: ColorsManager.primaryColorWithOpacity,
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 40.0,
|
||||
minWidth: 40.0,
|
||||
),
|
||||
isSelected:
|
||||
conditions.map((c) => c == (currentCondition ?? "==")).toList(),
|
||||
children: conditions.map((c) => Text(c)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateCondition(BuildContext context, String condition) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
condition: condition,
|
||||
value: selectedValue,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ValueDisplay extends StatelessWidget {
|
||||
final dynamic value;
|
||||
final String functionCode;
|
||||
|
||||
const _ValueDisplay({
|
||||
required this.value,
|
||||
required this.functionCode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
_getDisplayText(),
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getDisplayText() {
|
||||
final intValue = (value as num?)?.toInt() ?? 0;
|
||||
switch (functionCode) {
|
||||
case 'presence_time':
|
||||
return '$intValue Min';
|
||||
case 'dis_current':
|
||||
return '$intValue CM';
|
||||
case 'illuminance_value':
|
||||
return '$intValue Lux';
|
||||
default:
|
||||
return '$intValue';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _FunctionSlider extends StatelessWidget {
|
||||
final dynamic initialValue;
|
||||
final String functionCode;
|
||||
final DeviceFunctionData functionData;
|
||||
final AllDevicesModel? device;
|
||||
|
||||
const _FunctionSlider({
|
||||
required this.initialValue,
|
||||
required this.functionCode,
|
||||
required this.functionData,
|
||||
required this.device,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final (min, max) = _getSliderRange();
|
||||
return Slider(
|
||||
value: initialValue is int ? initialValue.toDouble() : min,
|
||||
min: min,
|
||||
max: max,
|
||||
divisions: (max - min).toInt(),
|
||||
onChanged: (value) => _updateValue(context, value.toInt()),
|
||||
);
|
||||
}
|
||||
|
||||
(double, double) _getSliderRange() {
|
||||
switch (functionCode) {
|
||||
case 'presence_time':
|
||||
return (0, 65535);
|
||||
case 'dis_current':
|
||||
return (1, 600);
|
||||
case 'illuminance_value':
|
||||
return (0, 10000);
|
||||
default:
|
||||
return (200, 300);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateValue(BuildContext context, int value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: functionCode,
|
||||
operationName: functionData.operationName,
|
||||
value: value,
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _OperationalValuesList extends StatelessWidget {
|
||||
final List<WpsOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
|
||||
const _OperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return operationName == 'Nobody Time'
|
||||
? _buildTimeWheel(context)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) =>
|
||||
_buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimeWheel(BuildContext context) {
|
||||
final currentTotalSeconds = selectedValue as int? ?? 0;
|
||||
return TimeWheelPicker(
|
||||
initialHours: currentTotalSeconds ~/ 3600,
|
||||
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
||||
initialSeconds: currentTotalSeconds % 60,
|
||||
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildValueIcon(context, value),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
||||
return Column(
|
||||
children: [
|
||||
if (_shouldShowTextDescription)
|
||||
Text(value.description.replaceAll("cm", '')),
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
bool get _shouldShowTextDescription =>
|
||||
operationName == 'Far Detection' ||
|
||||
operationName == 'Motionless Detection Sensitivity';
|
||||
|
||||
Widget _buildValueDescription(WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => _selectValue(context, value.value),
|
||||
);
|
||||
}
|
||||
|
||||
void _selectValue(BuildContext context, dynamic value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: h * 3600 + m * 60 + s,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_operational_value.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/time_wheel.dart';
|
||||
|
||||
class WpsOperationalValuesList extends StatelessWidget {
|
||||
final List<WpsOperationalValue> values;
|
||||
final dynamic selectedValue;
|
||||
final AllDevicesModel? device;
|
||||
final String operationName;
|
||||
final String selectCode;
|
||||
|
||||
const WpsOperationalValuesList({
|
||||
required this.values,
|
||||
required this.selectedValue,
|
||||
required this.device,
|
||||
required this.operationName,
|
||||
required this.selectCode,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return operationName == 'Nobody Time'
|
||||
? _buildTimeWheel(context)
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.all(20),
|
||||
itemCount: values.length,
|
||||
itemBuilder: (context, index) => _buildValueItem(context, values[index]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimeWheel(BuildContext context) {
|
||||
final currentTotalSeconds = selectedValue as int? ?? 0;
|
||||
return TimeWheelPicker(
|
||||
initialHours: currentTotalSeconds ~/ 3600,
|
||||
initialMinutes: (currentTotalSeconds % 3600) ~/ 60,
|
||||
initialSeconds: currentTotalSeconds % 60,
|
||||
onTimeChanged: (h, m, s) => _updateTotalSeconds(context, h, m, s),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueItem(BuildContext context, WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildValueIcon(context, value),
|
||||
Expanded(child: _buildValueDescription(value)),
|
||||
_buildValueRadio(context, value),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueIcon(context, WpsOperationalValue value) {
|
||||
return Column(
|
||||
children: [
|
||||
if (_shouldShowTextDescription) Text(value.description.replaceAll("cm", '')),
|
||||
SvgPicture.asset(value.icon, width: 25, height: 25),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
bool get _shouldShowTextDescription =>
|
||||
operationName == 'Far Detection' ||
|
||||
operationName == 'Motionless Detection Sensitivity';
|
||||
|
||||
Widget _buildValueDescription(WpsOperationalValue value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(value.description),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildValueRadio(context, WpsOperationalValue value) {
|
||||
return Radio<dynamic>(
|
||||
value: value.value,
|
||||
groupValue: selectedValue,
|
||||
onChanged: (_) => _selectValue(context, value.value),
|
||||
);
|
||||
}
|
||||
|
||||
void _selectValue(BuildContext context, dynamic value) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: value,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateTotalSeconds(BuildContext context, int h, int m, int s) {
|
||||
context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectCode,
|
||||
operationName: operationName,
|
||||
value: h * 3600 + m * 60 + s,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/routines/bloc/functions_bloc/functions_bloc_bloc.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/device_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/models/wps/wps_functions.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/wall_sensor/wps_operational_values_list.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/slider_value_selector.dart';
|
||||
|
||||
class WpsValueSelectorWidget extends StatelessWidget {
|
||||
final String selectedFunction;
|
||||
final DeviceFunctionData functionData;
|
||||
final List<WpsFunctions> wpsFunctions;
|
||||
final AllDevicesModel? device;
|
||||
final String dialogType;
|
||||
final bool removeComparators;
|
||||
|
||||
const WpsValueSelectorWidget({
|
||||
required this.selectedFunction,
|
||||
required this.functionData,
|
||||
required this.wpsFunctions,
|
||||
required this.device,
|
||||
required this.dialogType,
|
||||
required this.removeComparators,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final selectedFn = wpsFunctions.firstWhere((f) => f.code == selectedFunction);
|
||||
final values = selectedFn.getOperationalValues();
|
||||
|
||||
if (_isSliderFunction(selectedFunction)) {
|
||||
return SliderValueSelector(
|
||||
currentCondition: functionData.condition,
|
||||
dialogType: dialogType,
|
||||
sliderRange: sliderRange,
|
||||
displayedValue: getDisplayText,
|
||||
initialValue: functionData.value ?? 0.0,
|
||||
onConditionChanged: (condition) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
condition: condition,
|
||||
value: functionData.value ?? 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
onSliderChanged: (value) => context.read<FunctionBloc>().add(
|
||||
AddFunction(
|
||||
functionData: DeviceFunctionData(
|
||||
entityId: device?.uuid ?? '',
|
||||
functionCode: selectedFunction,
|
||||
operationName: functionData.operationName,
|
||||
value: value.toInt(),
|
||||
condition: functionData.condition,
|
||||
),
|
||||
),
|
||||
),
|
||||
unit: _unit,
|
||||
dividendOfRange: 1,
|
||||
);
|
||||
}
|
||||
|
||||
return WpsOperationalValuesList(
|
||||
values: values,
|
||||
selectedValue: functionData.value,
|
||||
device: device,
|
||||
operationName: selectedFn.operationName,
|
||||
selectCode: selectedFunction,
|
||||
);
|
||||
}
|
||||
|
||||
bool _isSliderFunction(String function) =>
|
||||
['dis_current', 'presence_time', 'illuminance_value'].contains(function);
|
||||
|
||||
(double, double) get sliderRange => switch (functionData.functionCode) {
|
||||
'presence_time' => (0, 65535),
|
||||
'dis_current' => (0, 600),
|
||||
'illuminance_value' => (0, 10000),
|
||||
_ => (200, 300),
|
||||
};
|
||||
|
||||
String get getDisplayText {
|
||||
final intValue = int.tryParse('${functionData.value ?? ''}');
|
||||
return switch (functionData.functionCode) {
|
||||
'presence_time' => '${intValue ?? '0'}',
|
||||
'dis_current' => '${intValue ?? '0'}',
|
||||
'illuminance_value' => '${intValue ?? '0'}',
|
||||
_ => '$intValue',
|
||||
};
|
||||
}
|
||||
|
||||
String get _unit => switch (functionData.functionCode) {
|
||||
'presence_time' => 'Min',
|
||||
'dis_current' => 'CM',
|
||||
'illuminance_value' => 'Lux',
|
||||
_ => '',
|
||||
};
|
||||
}
|
81
lib/pages/routines/widgets/slider_value_selector.dart
Normal file
@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/condition_toggle.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/function_slider.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/value_display.dart';
|
||||
|
||||
class SliderValueSelector extends StatelessWidget {
|
||||
final String? currentCondition;
|
||||
final String dialogType;
|
||||
final (double, double) sliderRange;
|
||||
final String displayedValue;
|
||||
final Object? initialValue;
|
||||
final void Function(String condition) onConditionChanged;
|
||||
final void Function(double value) onSliderChanged;
|
||||
final String unit;
|
||||
final double dividendOfRange;
|
||||
|
||||
const SliderValueSelector({
|
||||
required this.dialogType,
|
||||
required this.sliderRange,
|
||||
required this.displayedValue,
|
||||
required this.initialValue,
|
||||
required this.onConditionChanged,
|
||||
required this.onSliderChanged,
|
||||
required this.currentCondition,
|
||||
required this.unit,
|
||||
required this.dividendOfRange,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
spacing: 16,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (dialogType == 'IF')
|
||||
ConditionToggle(
|
||||
currentCondition: currentCondition,
|
||||
onChanged: onConditionChanged,
|
||||
),
|
||||
ValueDisplay(
|
||||
value: initialValue,
|
||||
label: displayedValue,
|
||||
unit: unit,
|
||||
),
|
||||
FunctionSlider(
|
||||
initialValue: initialValue,
|
||||
range: sliderRange,
|
||||
onChanged: onSliderChanged,
|
||||
dividendOfRange: dividendOfRange,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RangeInputFormatter extends TextInputFormatter {
|
||||
const RangeInputFormatter({required this.min, required this.max});
|
||||
|
||||
final double min;
|
||||
final double max;
|
||||
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
final text = newValue.text;
|
||||
if (text.isEmpty) {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
final value = double.tryParse(text);
|
||||
if (value == null || value < min || value > max) {
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
}
|
@ -3,10 +3,10 @@
|
||||
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/automation_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/helper/dialog_helper/device_dialog_helper.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/dragable_card.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/automation_dialog.dart';
|
||||
import 'package:syncrow_web/pages/routines/widgets/routine_dialogs/delay_dialog.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
@ -27,8 +27,7 @@ class ThenContainer extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('THEN',
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
state.isLoading && state.isUpdate == true
|
||||
? const Center(
|
||||
@ -41,12 +40,11 @@ class ThenContainer extends StatelessWidget {
|
||||
state.thenItems.length,
|
||||
(index) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (state.thenItems[index]
|
||||
['deviceId'] ==
|
||||
if (state.thenItems[index]['deviceId'] ==
|
||||
'delay') {
|
||||
final result = await DelayHelper
|
||||
.showDelayPickerDialog(context,
|
||||
state.thenItems[index]);
|
||||
.showDelayPickerDialog(
|
||||
context, state.thenItems[index]);
|
||||
|
||||
if (result != null) {
|
||||
context
|
||||
@ -66,17 +64,14 @@ class ThenContainer extends StatelessWidget {
|
||||
context: context,
|
||||
builder: (BuildContext context) =>
|
||||
AutomationDialog(
|
||||
automationName:
|
||||
state.thenItems[index]
|
||||
['name'] ??
|
||||
'Automation',
|
||||
automationId:
|
||||
state.thenItems[index]
|
||||
['deviceId'] ??
|
||||
'',
|
||||
uniqueCustomId:
|
||||
state.thenItems[index]
|
||||
['uniqueCustomId'],
|
||||
automationName: state.thenItems[index]
|
||||
['name'] ??
|
||||
'Automation',
|
||||
automationId: state.thenItems[index]
|
||||
['deviceId'] ??
|
||||
'',
|
||||
uniqueCustomId: state.thenItems[index]
|
||||
['uniqueCustomId'],
|
||||
),
|
||||
);
|
||||
|
||||
@ -85,13 +80,11 @@ class ThenContainer extends StatelessWidget {
|
||||
.read<RoutineBloc>()
|
||||
.add(AddToThenContainer({
|
||||
...state.thenItems[index],
|
||||
'imagePath':
|
||||
Assets.automation,
|
||||
'title':
|
||||
'imagePath': Assets.automation,
|
||||
'title': state.thenItems[index]
|
||||
['name'] ??
|
||||
state.thenItems[index]
|
||||
['name'] ??
|
||||
state.thenItems[index]
|
||||
['title'],
|
||||
['title'],
|
||||
}));
|
||||
}
|
||||
return;
|
||||
@ -114,9 +107,10 @@ class ThenContainer extends StatelessWidget {
|
||||
'2G',
|
||||
'3G',
|
||||
'WPS',
|
||||
'CPS',
|
||||
"GW",
|
||||
].contains(state.thenItems[index]
|
||||
['productType'])) {
|
||||
].contains(
|
||||
state.thenItems[index]['productType'])) {
|
||||
context.read<RoutineBloc>().add(
|
||||
AddToThenContainer(
|
||||
state.thenItems[index]));
|
||||
@ -126,9 +120,7 @@ class ThenContainer extends StatelessWidget {
|
||||
imagePath: state.thenItems[index]
|
||||
['imagePath'] ??
|
||||
'',
|
||||
title: state.thenItems[index]
|
||||
['title'] ??
|
||||
'',
|
||||
title: state.thenItems[index]['title'] ?? '',
|
||||
deviceData: state.thenItems[index],
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4, vertical: 8),
|
||||
@ -165,8 +157,8 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (mutableData['type'] == 'automation') {
|
||||
int index = state.thenItems.indexWhere(
|
||||
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||
int index = state.thenItems
|
||||
.indexWhere((item) => item['deviceId'] == mutableData['deviceId']);
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
@ -191,8 +183,8 @@ class ThenContainer extends StatelessWidget {
|
||||
}
|
||||
|
||||
if (mutableData['type'] == 'tap_to_run' && state.isAutomation) {
|
||||
int index = state.thenItems.indexWhere(
|
||||
(item) => item['deviceId'] == mutableData['deviceId']);
|
||||
int index = state.thenItems
|
||||
.indexWhere((item) => item['deviceId'] == mutableData['deviceId']);
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
@ -230,7 +222,7 @@ class ThenContainer extends StatelessWidget {
|
||||
dialogType: "THEN");
|
||||
if (result != null) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW']
|
||||
} else if (!['AC', '1G', '2G', '3G', 'WPS', 'GW', 'CPS']
|
||||
.contains(mutableData['productType'])) {
|
||||
context.read<RoutineBloc>().add(AddToThenContainer(mutableData));
|
||||
}
|
||||
|
33
lib/pages/routines/widgets/value_display.dart
Normal file
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ValueDisplay extends StatelessWidget {
|
||||
final dynamic value;
|
||||
final String label;
|
||||
final String unit;
|
||||
|
||||
const ValueDisplay({
|
||||
required this.value,
|
||||
required this.label,
|
||||
super.key,
|
||||
required this.unit,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.primaryColorWithOpacity.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
'$label $unit ',
|
||||
style: context.textTheme.headlineMedium!.copyWith(
|
||||
color: ColorsManager.primaryColorWithOpacity,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|