Compare commits
76 Commits
web_fix_bu
...
sep_bug_fi
Author | SHA1 | Date | |
---|---|---|---|
30dd3a1ee2 | |||
9dbf4b0540 | |||
0d49ad5106 | |||
9e5d5c0d84 | |||
92d32e4d84 | |||
0731507d3b | |||
58938d4598 | |||
e690ddc23a | |||
215fe60bc0 | |||
6568b26cb1 | |||
d47b0f4c71 | |||
f3f6369237 | |||
18265e7b06 | |||
6e3ad984e1 | |||
0c530e9ea6 | |||
b5eeeedcd2 | |||
2955533209 | |||
3a28f0ef9a | |||
b3d891b2c8 | |||
f99744338c | |||
66f45721e5 | |||
921ccf0132 | |||
26816b99cd | |||
facdef4741 | |||
adf553ee9e | |||
f2504e5f67 | |||
b3807f2980 | |||
ba95f6774b | |||
536ac8857e | |||
619d964cd7 | |||
6d805ddfd7 | |||
bc309adba7 | |||
67667e4405 | |||
c354abbeca | |||
fd09db6835 | |||
abb0a58468 | |||
7c28012d79 | |||
9876ff2e03 | |||
7d5b5340db | |||
185d94d800 | |||
db7a072ecf | |||
bcd49ad0e8 | |||
3c8d3feba3 | |||
e577cd3279 | |||
60b51657a6 | |||
b18b96064a | |||
b513f31214 | |||
7c97d01ddb | |||
28ec5beac9 | |||
46691dfff6 | |||
0ac3e79c30 | |||
ebd92c1011 | |||
b4932c644e | |||
222331f107 | |||
3171916faf | |||
28579e0778 | |||
dbb3450c32 | |||
c3d8e6a52e | |||
8c3df39cf4 | |||
7f862fac2a | |||
770db5383b | |||
eddd4828bb | |||
2162c996ea | |||
177baca864 | |||
a86c45a88b | |||
1f22e5d322 | |||
f6cc19cad7 | |||
e0f7c7ab39 | |||
ce861efa3f | |||
85a04e504f | |||
67ea7af595 | |||
b94717bc70 | |||
b565b646c1 | |||
0db7a9e1ba | |||
a4b1a9e85d | |||
316efd653c |
@ -31,13 +31,13 @@ jobs:
|
|||||||
run: flutter pub get
|
run: flutter pub get
|
||||||
|
|
||||||
- name: Build Flutter Web App
|
- name: Build Flutter Web App
|
||||||
run: flutter build web
|
run: flutter build web --release --dart-define=FLAVOR=development
|
||||||
|
|
||||||
- name: Build And Deploy
|
- name: Build And Deploy
|
||||||
id: builddeploy
|
id: builddeploy
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
uses: Azure/static-web-apps-deploy@v1
|
||||||
with:
|
with:
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }}
|
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_SMOKE_017C65C10 }}
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
|
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
|
||||||
action: "upload"
|
action: "upload"
|
||||||
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
|
###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
|
||||||
@ -56,5 +56,5 @@ jobs:
|
|||||||
id: closepullrequest
|
id: closepullrequest
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
uses: Azure/static-web-apps-deploy@v1
|
||||||
with:
|
with:
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }}
|
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_POLITE_SMOKE_017C65C10 }}
|
||||||
action: "close"
|
action: "close"
|
@ -1,65 +0,0 @@
|
|||||||
name: Azure Static Web Apps CI/CD
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, closed]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_and_deploy_job:
|
|
||||||
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Build and Deploy Job
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
lfs: false
|
|
||||||
|
|
||||||
- name: Set up Flutter
|
|
||||||
uses: subosito/flutter-action@v2
|
|
||||||
with:
|
|
||||||
flutter-version: '3.22.2' # Specify the Flutter version you want to use
|
|
||||||
|
|
||||||
- name: Load Environment Variables
|
|
||||||
run: |
|
|
||||||
echo "Loading environment variables from .env.production"
|
|
||||||
cat .env.production | grep -v '^#' >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: flutter pub get
|
|
||||||
|
|
||||||
- name: Build Flutter Web App
|
|
||||||
run: |
|
|
||||||
flutter build web --release \
|
|
||||||
--dart-define=ENV_NAME=${{ env.ENV_NAME }} \
|
|
||||||
--dart-define=BASE_URL=${{ env.BASE_URL }} \
|
|
||||||
--dart-define=FLAVOR=production
|
|
||||||
|
|
||||||
- name: Build And Deploy
|
|
||||||
id: builddeploy
|
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
|
||||||
with:
|
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }}
|
|
||||||
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
|
|
||||||
action: "upload"
|
|
||||||
app_location: "." # Root of your Flutter project
|
|
||||||
api_location: "" # API source code path - optional
|
|
||||||
output_location: "build/web" # Path to the built web app content directory
|
|
||||||
|
|
||||||
close_pull_request_job:
|
|
||||||
if: github.event_name == 'pull_request' && github.event.action == 'closed'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
name: Close Pull Request Job
|
|
||||||
steps:
|
|
||||||
- name: Close Pull Request
|
|
||||||
id: closepullrequest
|
|
||||||
uses: Azure/static-web-apps-deploy@v1
|
|
||||||
with:
|
|
||||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_MUSHROOM_0D31A3303 }}
|
|
||||||
action: "close"
|
|
10
README.md
@ -1,7 +1,5 @@
|
|||||||
# syncrow_web
|
# syncrow_web
|
||||||
|
|
||||||
A new Flutter project.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
This project is a starting point for a Flutter application.
|
This project is a starting point for a Flutter application.
|
||||||
@ -14,3 +12,11 @@ A few resources to get you started if this is your first Flutter project:
|
|||||||
For help getting started with Flutter development, view the
|
For help getting started with Flutter development, view the
|
||||||
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
[online documentation](https://docs.flutter.dev/), which offers tutorials,
|
||||||
samples, guidance on mobile development, and a full API reference.
|
samples, guidance on mobile development, and a full API reference.
|
||||||
|
|
||||||
|
|
||||||
|
## USEFUL COMMANDS
|
||||||
|
|
||||||
|
Run on chrome: flutter run -d chrome --dart-define=FLAVOR='ENV_NAME'
|
||||||
|
|
||||||
|
Build: flutter build web --release --dart-define=FLAVOR='ENV_NAME'
|
||||||
|
|
||||||
|
5
assets/icons/ac_lock.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="25" height="35" viewBox="0 0 25 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12.3952 0C7.16271 0.00642578 2.92244 4.24662 2.91602 9.47919V15.3125C2.91602 15.7152 3.2425 16.0417 3.64521 16.0417H6.5619C6.9646 16.0417 7.29109 15.7152 7.29109 15.3125V9.47919C7.29102 6.66019 9.57627 4.375 12.3952 4.375C15.2141 4.375 17.4994 6.66019 17.4994 9.47919V15.3125C17.4994 15.7152 17.8259 16.0417 18.2286 16.0417H21.1452C21.5479 16.0417 21.8744 15.7152 21.8744 15.3125V9.47919C21.868 4.24662 17.6277 0.00642578 12.3952 0Z" fill="#455A64"/>
|
||||||
|
<path d="M3.64581 14.5833H21.1458C23.1593 14.5833 24.7916 16.2155 24.7916 18.2291V31.3541C24.7916 33.3677 23.1593 34.9999 21.1458 34.9999H3.64581C1.63229 34.9999 0 33.3677 0 31.3541V18.2291C0 16.2155 1.63229 14.5833 3.64581 14.5833Z" fill="#2F66D3"/>
|
||||||
|
<path d="M16.0417 22.6041C16.0501 20.5906 14.4248 18.9514 12.4112 18.9429C10.3977 18.9344 8.75851 20.5598 8.75003 22.5733C8.74415 23.9651 9.53118 25.2388 10.7785 25.8562L10.2156 29.7937C10.1592 30.1924 10.4367 30.5613 10.8355 30.6177C10.8692 30.6225 10.9034 30.6249 10.9375 30.6249H13.8542C14.2569 30.629 14.5866 30.3059 14.5907 29.9032C14.591 29.8661 14.5886 29.8289 14.5833 29.7922L14.0203 25.8547C15.2524 25.2381 16.0337 23.9817 16.0417 22.6041Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
28
assets/icons/ac_schedule.svg
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.6724 33.345C25.8803 33.345 33.3447 25.8805 33.3447 16.6726C33.3447 7.46472 25.8803 0.000244141 16.6724 0.000244141C7.46448 0.000244141 0 7.46472 0 16.6726C0 25.8805 7.46448 33.345 16.6724 33.345Z" fill="#F07281"/>
|
||||||
|
<path d="M17.3767 33.3303C17.1436 33.3401 16.9084 33.345 16.6725 33.345C7.46478 33.345 0 25.8802 0 16.6725C0 7.46478 7.46478 0 16.6725 0C16.9084 0 17.1436 0.00492188 17.3767 0.0147656C8.4957 0.38377 1.40841 7.70068 1.40841 16.6725C1.40841 25.6443 8.4957 32.9613 17.3767 33.3303Z" fill="#EB5569"/>
|
||||||
|
<path d="M16.6726 31.1577C24.6725 31.1577 31.1577 24.6725 31.1577 16.6726C31.1577 8.67269 24.6725 2.1875 16.6726 2.1875C8.67269 2.1875 2.1875 8.67269 2.1875 16.6726C2.1875 24.6725 8.67269 31.1577 16.6726 31.1577Z" fill="#EAF6FF"/>
|
||||||
|
<path d="M17.7291 31.1197C17.3805 31.145 17.0276 31.1577 16.6727 31.1577C8.67269 31.1577 2.1875 24.6725 2.1875 16.6725C2.1875 8.67244 8.67275 2.18726 16.6728 2.18726C17.0277 2.18726 17.3805 2.1999 17.7291 2.22526C10.2221 2.76619 4.30022 9.02743 4.30022 16.6725C4.30022 24.3175 10.2221 30.5788 17.7291 31.1197Z" fill="#D8ECFE"/>
|
||||||
|
<path d="M15.7672 8.23932L16.4138 7.82301C16.5715 7.72143 16.7741 7.72143 16.9319 7.82301L17.5785 8.23932C17.6957 8.31479 17.7666 8.44474 17.7666 8.58419V16.8661H15.5791V8.58419C15.579 8.44474 15.6499 8.31486 15.7672 8.23932Z" fill="#5680A6"/>
|
||||||
|
<path d="M17.377 8.10996L16.9876 8.36063V16.8662H15.5791V8.62163C15.5791 8.45893 15.6617 8.30738 15.7985 8.21933L16.4136 7.82326C16.5714 7.72167 16.774 7.72167 16.9318 7.82326L17.377 8.10996Z" fill="#497090"/>
|
||||||
|
<path d="M23.5067 16.9316L23.1106 17.5467C23.0225 17.6835 22.871 17.7662 22.7083 17.7662H16.6729V15.5789H22.7083C22.871 15.5789 23.0225 15.6615 23.1106 15.7983L23.5067 16.4135C23.6083 16.5712 23.6083 16.7738 23.5067 16.9316Z" fill="#5680A6"/>
|
||||||
|
<path d="M19.6131 16.6725C19.6131 17.0591 19.5384 17.4281 19.4018 17.7662H16.6729V15.5789H19.4018C19.5384 15.9169 19.6131 16.2859 19.6131 16.6725Z" fill="#497090"/>
|
||||||
|
<path d="M16.6723 18.2039C17.5182 18.2039 18.2039 17.5182 18.2039 16.6723C18.2039 15.8264 17.5182 15.1406 16.6723 15.1406C15.8264 15.1406 15.1406 15.8264 15.1406 16.6723C15.1406 17.5182 15.8264 18.2039 16.6723 18.2039Z" fill="#F07281"/>
|
||||||
|
<path d="M17.3765 18.0323C17.166 18.1422 16.9265 18.2042 16.6723 18.2042C15.8265 18.2042 15.1406 17.5183 15.1406 16.6725C15.1406 15.8268 15.8265 15.1409 16.6723 15.1409C16.9265 15.1409 17.166 15.2029 17.3765 15.3127C16.885 15.567 16.549 16.081 16.549 16.6726C16.549 17.2642 16.885 17.7781 17.3765 18.0323Z" fill="#EB5569"/>
|
||||||
|
<path d="M34.5329 28.7151V27.6225C34.5329 27.4043 34.3853 27.2137 34.174 27.1592L32.9876 26.853C32.8555 26.3207 32.6457 25.8193 32.37 25.3617L32.9923 24.3064C33.1032 24.1184 33.0728 23.8793 32.9185 23.7249L32.1459 22.9523C31.9916 22.798 31.7524 22.7676 31.5644 22.8785L30.5091 23.5008C30.0516 23.2251 29.5501 23.0153 29.0178 22.8832L28.7116 21.6968C28.6571 21.4855 28.4665 21.3379 28.2483 21.3379H27.1557C26.9375 21.3379 26.7469 21.4855 26.6924 21.6968L26.3862 22.8832C25.8539 23.0153 25.3525 23.2252 24.8949 23.5008L23.8396 22.8785C23.6516 22.7676 23.4125 22.798 23.2581 22.9523L22.4855 23.7249C22.3313 23.8792 22.3009 24.1184 22.4117 24.3064L23.0341 25.3617C22.7583 25.8193 22.5485 26.3207 22.4164 26.853L21.23 27.1592C21.0188 27.2137 20.8711 27.4043 20.8711 27.6225V28.7151C20.8711 28.9333 21.0188 29.1239 21.23 29.1784L22.4164 29.4846C22.5485 30.0169 22.7584 30.5183 23.0341 30.9759L22.4117 32.0312C22.3008 32.2192 22.3313 32.4583 22.4855 32.6127L23.2581 33.3853C23.4124 33.5396 23.6516 33.5699 23.8396 33.4591L24.8949 32.8368C25.3525 33.1125 25.8539 33.3223 26.3862 33.4544L26.6924 34.6408C26.7469 34.8521 26.9375 34.9997 27.1557 34.9997H28.2483C28.4665 34.9997 28.6571 34.8521 28.7116 34.6408L29.0178 33.4544C29.5501 33.3223 30.0516 33.1124 30.5091 32.8368L31.5644 33.4591C31.7524 33.57 31.9915 33.5396 32.1459 33.3853L32.9185 32.6127C33.0728 32.4584 33.1031 32.2192 32.9923 32.0312L32.37 30.9759C32.6457 30.5183 32.8555 30.0169 32.9876 29.4846L34.174 29.1784C34.3853 29.1239 34.5329 28.9333 34.5329 28.7151Z" fill="#88B4F5"/>
|
||||||
|
<path d="M22.2796 27.252V29.0858L23.8246 29.4844C23.9563 30.0168 24.1662 30.5182 24.4423 30.976L23.6317 32.3506L24.4057 33.1246L23.8391 33.4588C23.6511 33.5696 23.4119 33.5393 23.2576 33.3849L22.4853 32.6127C22.331 32.4584 22.3007 32.2192 22.4115 32.0313L23.0338 30.976C22.7577 30.5182 22.5479 30.0168 22.4162 29.4844L21.23 29.1784C21.0187 29.124 20.8711 28.9333 20.8711 28.7151V27.6226C20.8711 27.4044 21.0187 27.2139 21.2299 27.1593L22.4162 26.8527C22.5479 26.321 22.7577 25.8189 23.0338 25.3619L22.4115 24.3064C22.3007 24.1185 22.331 23.8794 22.4853 23.7251L23.2577 22.9523C23.4119 22.7978 23.6512 22.7675 23.8392 22.8784L24.4057 23.2125L23.6317 23.9872L24.4423 25.3619C24.1662 25.8189 23.9564 26.321 23.8246 26.8527L22.2796 27.252Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.7946 33.4542L28.1932 35H27.1555C26.9372 35 26.7466 34.8523 26.6921 34.6409L26.3861 33.4542C26.1361 33.393 25.8924 33.3134 25.6572 33.2176L26.303 32.8367C26.7608 33.1126 27.2622 33.3225 27.7946 33.4542Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.1555 21.3379H28.1932L27.7946 22.883C27.2622 23.0154 26.7608 23.2252 26.303 23.5006L25.6572 23.1196C25.8925 23.0245 26.1361 22.945 26.3861 22.883L26.6921 21.6969C26.7466 21.4855 26.9373 21.3379 27.1555 21.3379Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.7023 31.4612C29.5205 31.4612 30.9944 29.9873 30.9944 28.1691C30.9944 26.3509 29.5205 24.877 27.7023 24.877C25.8841 24.877 24.4102 26.3509 24.4102 28.1691C24.4102 29.9873 25.8841 31.4612 27.7023 31.4612Z" fill="#5680A6"/>
|
||||||
|
<path d="M28.7581 31.2882C28.4264 31.4009 28.0707 31.4615 27.7017 31.4615C25.8841 31.4615 24.4102 29.9875 24.4102 28.1692C24.4102 26.3509 25.8841 24.877 27.7017 24.877C28.0707 24.877 28.4263 24.9375 28.7581 25.0502C27.4588 25.4903 26.5229 26.7206 26.5229 28.1692C26.5229 29.6178 27.4588 30.8481 28.7581 31.2882Z" fill="#497090"/>
|
||||||
|
<path d="M16.6727 6.19289C16.381 6.19289 16.1445 5.95644 16.1445 5.66475V4.30036C16.1445 4.00867 16.381 3.77222 16.6727 3.77222C16.9644 3.77222 17.2008 4.00867 17.2008 4.30036V5.66468C17.2008 5.95637 16.9644 6.19289 16.6727 6.19289Z" fill="#88B4F5"/>
|
||||||
|
<path d="M16.6727 29.5729C16.381 29.5729 16.1445 29.3365 16.1445 29.0448V27.6805C16.1445 27.3888 16.381 27.1523 16.6727 27.1523C16.9644 27.1523 17.2008 27.3888 17.2008 27.6805V29.0448C17.2008 29.3364 16.9644 29.5729 16.6727 29.5729Z" fill="#88B4F5"/>
|
||||||
|
<path d="M5.66492 17.2006H4.30061C4.00892 17.2006 3.77246 16.9641 3.77246 16.6724C3.77246 16.3807 4.00892 16.1443 4.30061 16.1443H5.66492C5.95661 16.1443 6.19307 16.3807 6.19307 16.6724C6.19307 16.9641 5.95661 17.2006 5.66492 17.2006Z" fill="#88B4F5"/>
|
||||||
|
<path d="M29.0448 17.2006H27.6805C27.3887 17.2006 27.1523 16.9641 27.1523 16.6724C27.1523 16.3807 27.3888 16.1443 27.6805 16.1443H29.0448C29.3366 16.1443 29.5729 16.3807 29.5729 16.6724C29.5729 16.9641 29.3365 17.2006 29.0448 17.2006Z" fill="#88B4F5"/>
|
||||||
|
<path d="M5.95843 23.3868C5.77591 23.3868 5.59838 23.2921 5.50056 23.1226C5.35468 22.87 5.44122 22.547 5.69388 22.4012L6.8754 21.719C7.12785 21.5733 7.45099 21.6597 7.59687 21.9123C7.74275 22.1649 7.6562 22.488 7.40355 22.6338L6.22202 23.3159C6.1389 23.364 6.04805 23.3868 5.95843 23.3868Z" fill="#88B4F5"/>
|
||||||
|
<path d="M26.2065 11.6969C26.0239 11.6969 25.8464 11.6022 25.7486 11.4327C25.6027 11.1801 25.6893 10.8571 25.9419 10.7113L27.1234 10.0291C27.376 9.88328 27.699 9.96976 27.8449 10.2224C27.9908 10.475 27.9042 10.7981 27.6516 10.9439L26.4701 11.626C26.3869 11.6741 26.296 11.6969 26.2065 11.6969Z" fill="#88B4F5"/>
|
||||||
|
<path d="M7.139 11.6967C7.04938 11.6967 6.9586 11.6739 6.8754 11.6259L5.69388 10.9437C5.44122 10.7978 5.35468 10.4748 5.50056 10.2222C5.64637 9.96957 5.96937 9.88296 6.22202 10.0289L7.40355 10.7111C7.6562 10.8569 7.74275 11.1799 7.59687 11.4325C7.49904 11.602 7.32152 11.6967 7.139 11.6967Z" fill="#88B4F5"/>
|
||||||
|
<path d="M10.4858 27.9156C10.3962 27.9156 10.3054 27.8928 10.2222 27.8447C9.96954 27.6988 9.883 27.3758 10.0289 27.1232L10.711 25.9417C10.8568 25.6891 11.1799 25.6024 11.4325 25.7484C11.6852 25.8943 11.7717 26.2173 11.6258 26.4699L10.9437 27.6514C10.8458 27.8208 10.6683 27.9156 10.4858 27.9156Z" fill="#88B4F5"/>
|
||||||
|
<path d="M22.1762 7.66776C22.0866 7.66776 21.9958 7.64492 21.9126 7.59687C21.66 7.45106 21.5734 7.12799 21.7193 6.8754L22.4015 5.69388C22.5473 5.44122 22.8702 5.35468 23.1229 5.50056C23.3755 5.64637 23.4621 5.96944 23.3162 6.22202L22.6341 7.40355C22.5363 7.57301 22.3587 7.66776 22.1762 7.66776Z" fill="#88B4F5"/>
|
||||||
|
<path d="M11.1689 7.66776C10.9864 7.66776 10.8089 7.57301 10.711 7.40355L10.0289 6.22202C9.883 5.96944 9.96954 5.64637 10.2222 5.50056C10.4747 5.35468 10.7979 5.44122 10.9437 5.69388L11.6258 6.8754C11.7717 7.12799 11.6852 7.45106 11.4325 7.59687C11.3494 7.64486 11.2585 7.66776 11.1689 7.66776Z" fill="#88B4F5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
15
assets/icons/empty_records.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M6.92516 12.1457C6.6709 11.6094 6.03272 11.3809 5.49988 11.6364L0.830003 13.8763C0.355091 13.9861 0 14.4121 0 14.9238V20.3082C0 20.9031 0.478601 21.385 1.06937 21.385C1.66001 21.385 2.13847 20.9031 2.13847 20.3082V16.5989L5.49988 18.2112C5.64826 18.2825 5.80469 18.3161 5.95867 18.3161C6.35844 18.3161 6.74154 18.0893 6.92516 17.7017C7.1786 17.1648 6.95249 16.5222 6.41937 16.2664L3.61977 14.9237L6.41937 13.581C6.95262 13.3253 7.1786 12.6828 6.92516 12.1457Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M12.3651 10.6137C12.5194 10.6137 12.6755 10.5802 12.8243 10.5089L19.2289 7.43717C19.762 7.1812 19.9886 6.53854 19.7347 6.00156C19.481 5.46528 18.8432 5.23697 18.3094 5.49225L11.9046 8.56423C11.3714 8.82006 11.1453 9.46245 11.3989 9.99956C11.5824 10.3868 11.9655 10.6137 12.3651 10.6137Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M25.6338 4.36467L28.9106 2.79269V6.54683C28.9106 7.14161 29.3893 7.62367 29.9797 7.62367C30.5704 7.62367 31.0488 7.14161 31.0488 6.54683V2.77315L34.3669 4.36467C34.5157 4.43582 34.6719 4.46953 34.8261 4.46953C35.2256 4.46953 35.6089 4.24274 35.7922 3.85522C36.0459 3.31825 35.8198 2.67572 35.2866 2.41989L30.46 0.104656C30.169 -0.0348854 29.8315 -0.0348854 29.5405 0.104656L24.7142 2.41989C24.1811 2.67572 23.9548 3.31811 24.2085 3.85522C24.4628 4.39261 25.1019 4.62119 25.6338 4.36467Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M40.7719 7.43681L47.1768 10.5089C47.3249 10.5802 47.4816 10.6138 47.6358 10.6138C48.0353 10.6138 48.4186 10.387 48.6019 9.99948C48.8555 9.4625 48.6295 8.81998 48.0963 8.56415L41.6912 5.49189C41.1588 5.23648 40.5206 5.46451 40.2662 6.0012C40.0124 6.53818 40.2386 7.1807 40.7719 7.43681Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M48.9237 20.886C49.4568 20.6303 49.683 19.9878 49.4292 19.4506C49.1755 18.9139 48.537 18.6855 48.0042 18.9417L43.9005 20.9106L31.0489 14.7463V9.94598C31.0489 9.35079 30.5704 8.86914 29.9798 8.86914C29.3893 8.86914 28.9107 9.35093 28.9107 9.94598V14.7658L16.101 20.9101L12.4202 19.1445C11.8858 18.8886 11.2492 19.1168 10.9949 19.6539C10.7412 20.1909 10.9674 20.8335 11.5006 21.0894L15.4821 22.9992V37.0011L11.4358 38.9419C10.9027 39.1977 10.6766 39.8401 10.9301 40.3774C11.1135 40.7648 11.4966 40.9915 11.8962 40.9915C12.0505 40.9915 12.2071 40.958 12.3553 40.8867L16.1012 39.09L28.9107 45.2342V48.9046C28.9107 49.4996 29.3893 49.9814 29.9798 49.9814C30.5704 49.9814 31.0489 49.4995 31.0489 48.9046V45.254L43.8996 39.0901L47.7917 40.957C47.9404 41.0282 48.0967 41.0619 48.2508 41.0619C48.6503 41.0619 49.0335 40.8351 49.2169 40.4476C49.4707 39.9106 49.2443 39.2681 48.7112 39.0121L44.5188 37.0013V22.9992L48.9237 20.886ZM42.3806 37.4328L31.049 42.8681V29.4597L42.3806 24.0241V37.4328ZM17.6875 37.1364C17.6685 37.0962 17.6434 37.0615 17.6203 37.0249V24.0243L28.9107 29.4399V42.8485L17.7848 37.5119C17.7746 37.3852 17.7449 37.2579 17.6875 37.1364ZM30.0004 16.6291L41.4126 22.103L30.0004 27.5767L18.5882 22.103L30.0004 16.6291Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M47.1767 49.4916L40.772 52.5637C40.2389 52.8196 40.0123 53.4622 40.2662 53.9987C40.4497 54.3864 40.8329 54.6129 41.2324 54.6129C41.3865 54.6129 41.5432 54.5793 41.6915 54.5086L48.0964 51.4363C48.6295 51.1805 48.8556 50.5377 48.602 50.0007C48.3479 49.464 47.7094 49.237 47.1767 49.4916Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M34.367 55.636L31.0489 57.2276V52.9362C31.0489 52.3414 30.5704 51.8594 29.9798 51.8594C29.3893 51.8594 28.9107 52.3414 28.9107 52.9362V57.2081L25.6338 55.636C25.1002 55.3814 24.4628 55.6084 24.2086 56.1455C23.9548 56.6825 24.181 57.3248 24.7142 57.5806L29.5408 59.8956C29.6862 59.965 29.8433 60 30.0005 60C30.1578 60 30.3149 59.965 30.4603 59.8956L35.2869 57.5806C35.82 57.3248 36.0462 56.6825 35.7925 56.1455C35.5383 55.6084 34.8997 55.3819 34.367 55.636Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M19.2289 52.5636L12.824 49.4914C12.2922 49.2362 11.6534 49.4635 11.3989 50.0005C11.1453 50.5375 11.3714 51.1803 11.9046 51.4361L18.3096 54.5083C18.4577 54.5791 18.6144 54.6127 18.7685 54.6127C19.168 54.6127 19.5512 54.3861 19.7346 53.9985C19.9883 53.4621 19.762 52.8195 19.2289 52.5636Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M6.92516 42.2985C6.6709 41.7618 6.03272 41.5345 5.49988 41.7894L2.13847 43.4014V39.3708C2.13847 38.776 1.66001 38.2939 1.06937 38.2939C0.478601 38.2939 0 38.776 0 39.3708V44.7553C0 44.8898 0.0274617 45.0171 0.0724116 45.1359C0.0941352 45.5271 0.319158 45.8792 0.673293 46.0493L5.49988 48.3645C5.64826 48.4354 5.80469 48.4692 5.95867 48.4692C6.35844 48.4692 6.74154 48.2424 6.92516 47.855C7.1786 47.3181 6.95249 46.6755 6.41937 46.4194L3.61977 45.077L6.41937 43.7343C6.95262 43.4783 7.1786 42.8355 6.92516 42.2985Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M1.06937 34.0934C1.66001 34.0934 2.13847 33.6114 2.13847 33.0163V26.6623C2.13847 26.0675 1.66001 25.5854 1.06937 25.5854C0.478601 25.5854 0 26.0675 0 26.6623V33.0163C0.000136626 33.6114 0.478738 34.0934 1.06937 34.0934Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M59.1707 13.8762L54.5008 11.6362C53.9669 11.3801 53.3298 11.6088 53.0757 12.1455C52.8221 12.6828 53.0482 13.3252 53.5813 13.5811L56.3809 14.9239L53.5813 16.2666C53.0482 16.5224 52.8221 17.1648 53.0757 17.7019C53.259 18.0894 53.6422 18.3162 54.0417 18.3162C54.196 18.3162 54.3527 18.2826 54.5008 18.2113L57.8622 16.599V20.3083C57.8622 20.9032 58.3407 21.3852 58.9313 21.3852C59.5219 21.3852 60.0003 20.9032 60.0003 20.3083V14.924C60.0003 14.4119 59.6456 13.986 59.1707 13.8762Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M58.9314 25.5854C58.3406 25.5854 57.8623 26.0675 57.8623 26.6623V33.0163C57.8623 33.6114 58.3408 34.0931 58.9314 34.0931C59.522 34.0931 60.0004 33.6113 60.0004 33.0163V26.6623C60.0004 26.0675 59.5222 25.5854 58.9314 25.5854Z" fill="#D5D5D5"/>
|
||||||
|
<path d="M58.9313 38.2935C58.3405 38.2935 57.8622 38.7755 57.8622 39.3703V43.4009L54.5008 41.7889C53.9669 41.5336 53.3298 41.7611 53.0757 42.2981C52.8221 42.835 53.0482 43.4778 53.5813 43.7337L56.3809 45.0764L53.5813 46.4188C53.0482 46.6749 52.8221 47.3174 53.0757 47.8544C53.259 48.2418 53.6422 48.4686 54.0417 48.4686C54.196 48.4686 54.3527 48.4347 54.5008 48.3639L59.3274 46.0486C59.6815 45.8785 59.9065 45.5265 59.9283 45.1353C59.9731 45.0165 60.0003 44.8892 60.0003 44.7546V39.3702C60.0003 38.7755 59.5221 38.2935 58.9313 38.2935Z" fill="#D5D5D5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 6.0 KiB |
@ -1,10 +1,4 @@
|
|||||||
<svg width="17" height="22" viewBox="0 0 17 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="35" height="30" viewBox="0 0 35 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M8.771 11.6328H8.13623L7.81885 11L8.13623 10.3672H8.771V11.6328Z" fill="#292827"/>
|
<path d="M27.4702 3.86642L24.351 7.51614C22.56 5.82834 20.1489 4.79201 17.5 4.79201C11.9882 4.79201 7.50309 9.27708 7.50309 14.7889C7.50309 15.0111 7.5113 15.2319 7.52565 15.4506H9.73845C10.4774 15.4506 10.8725 16.3209 10.3872 16.8773L8.47038 19.0757L5.30055 22.7125L4.41734 21.6994L0.213911 16.8773C-0.272125 16.3209 0.123677 15.4506 0.862644 15.4506H2.72613C2.71656 15.2312 2.71109 15.0104 2.71109 14.7889C2.71109 10.8384 4.24918 7.12444 7.04235 4.33126C9.83552 1.53809 13.5495 0 17.5 0C21.2249 0 24.7393 1.36856 27.4702 3.86642Z" fill="#619CED"/>
|
||||||
<path d="M7.50146 10.3672H8.13623V11.6328H7.50146V10.3672Z" fill="#464443"/>
|
<path d="M34.1379 15.491H32.273C32.1021 19.1776 30.5852 22.6188 27.9582 25.2466C25.165 28.0397 21.451 29.5778 17.5005 29.5778C14.1003 29.5778 10.8758 28.4376 8.26172 26.3383L11.3817 22.6886C13.0742 24.0024 15.1975 24.7858 17.5005 24.7858C22.7765 24.7858 27.1105 20.6767 27.4721 15.491H25.2621C24.5231 15.491 24.128 14.6214 24.6133 14.0643L26.9143 11.425L29.7 8.22914L31.6516 10.4679L34.7866 14.0643C35.2726 14.6214 34.8768 15.491 34.1379 15.491Z" fill="#619CED"/>
|
||||||
<path d="M4.8042 10.3672H6.07373V11.6328H4.8042V10.3672Z" fill="#464443"/>
|
|
||||||
<path d="M10.1987 10.3672H11.4683V11.6328H10.1987V10.3672Z" fill="#292827"/>
|
|
||||||
<path d="M8.13626 2.88879C3.64275 2.88879 0 6.52029 0 11C0 13.0387 0.754651 14.9015 2.00036 16.3265L3.9143 14.4184C3.15343 13.4856 2.69708 12.296 2.69708 11C2.69708 8.00525 5.13225 5.57757 8.13621 5.57757H8.8105V2.88879H8.13626Z" fill="#30BBEC"/>
|
|
||||||
<path d="M14.2725 5.67358L12.3586 7.58168C13.1195 8.51449 13.5758 9.70409 13.5758 11.0001C13.5758 13.9949 11.1407 16.4225 8.13669 16.4225H7.4624V19.1113H8.13665C12.6302 19.1113 16.2729 15.4798 16.2729 11.0001C16.2729 8.96142 15.5183 7.09859 14.2725 5.67358Z" fill="#1F8DCD"/>
|
|
||||||
<path d="M8.13623 0.199951V8.26637L12.1819 4.23316L8.13623 0.199951Z" fill="#16A5D9"/>
|
|
||||||
<path d="M8.13648 21.8V13.7336L4.09082 17.7668L8.13648 21.8Z" fill="#16A5D9"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
8
assets/icons/firmware.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg width="32" height="35" viewBox="0 0 32 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.20859 17.5333C2.20859 22.0298 4.44255 26.0045 7.86104 28.408C8.46346 28.8318 8.4955 29.7117 7.91685 30.1672L7.90056 30.18C7.52272 30.4775 6.99293 30.4973 6.59933 30.2209C2.6086 27.4182 0 22.7802 0 17.5333C0 9.02288 7.04447 2.11406 15.5378 2.04143V4.25055C8.26345 4.32265 2.20859 10.2421 2.20859 17.5333Z" fill="#1ED688"/>
|
||||||
|
<path d="M15.9433 0.065948L20.0021 2.84224C20.2195 2.99097 20.2195 3.31167 20.0021 3.46041L15.9433 6.2367C15.6947 6.4068 15.3574 6.22869 15.3574 5.92775V0.375166C15.3574 0.0739588 15.6947 -0.103882 15.9433 0.065948Z" fill="#1AB975"/>
|
||||||
|
<path d="M29.0795 17.4666C29.0795 12.9701 26.8453 8.99537 23.4268 6.59185C22.8243 6.16808 22.7923 5.28822 23.371 4.83267L23.3872 4.81985C23.7651 4.52238 24.2949 4.50262 24.6885 4.779C28.6795 7.58172 31.2878 12.2197 31.2878 17.4666C31.2878 25.977 24.2553 32.8858 15.7617 32.9585V30.7493C23.0361 30.6772 29.0795 24.758 29.0795 17.4666Z" fill="#1ED688"/>
|
||||||
|
<path d="M15.3449 34.9339L11.2861 32.1576C11.0687 32.0089 11.0687 31.6882 11.2861 31.5395L15.3449 28.7632C15.5932 28.5931 15.9308 28.7712 15.9308 29.0721V34.6247C15.9308 34.9259 15.5932 35.104 15.3449 34.9339Z" fill="#1AB975"/>
|
||||||
|
<path d="M22.5325 15.7787L15.884 21.2923L14.2079 22.6824C13.813 23.0095 13.3131 23.1871 12.8047 23.1871C12.72 23.1871 12.6354 23.1823 12.5499 23.1724C11.9568 23.1027 11.411 22.7882 11.053 22.3099L8.46759 18.8637C7.88387 18.0853 8.04168 16.9803 8.82034 16.3955C9.59953 15.8118 10.7045 15.9696 11.2885 16.7483L13.0338 19.0754L13.0479 19.0639L20.2822 13.0643C21.0318 12.4432 22.1432 12.5468 22.7645 13.2964C23.3859 14.0459 23.282 15.1573 22.5325 15.7787Z" fill="#1ED688"/>
|
||||||
|
<path d="M22.5334 15.7787L15.8849 21.2923C14.7976 20.7494 13.8414 19.9991 13.0488 19.0639L20.2832 13.0643C21.0327 12.4432 22.1441 12.5468 22.7654 13.2964C23.3868 14.0459 23.2829 15.1573 22.5334 15.7787Z" fill="#35E298"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
23
assets/icons/main_door.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<svg width="29" height="38" viewBox="0 0 29 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_i_2351_2766)">
|
||||||
|
<path d="M0 5.18168C0 2.69345 1.82822 0.560776 4.30407 0.312864C8.58098 -0.115392 11.9929 -0.0966095 16.2213 0.32393C18.7091 0.57135 20.5548 2.70894 20.5548 5.20899V32.7835C20.5548 35.246 18.7637 37.3665 16.3165 37.6408C12.0557 38.1183 8.5855 38.1222 4.24256 37.6376C1.79332 37.3643 0 35.2435 0 32.7791V5.18168Z" fill="#EAF6FF"/>
|
||||||
|
</g>
|
||||||
|
<path d="M17.5761 23.3577C17.7051 23.3577 17.8341 23.3086 17.9325 23.2102C18.1293 23.0133 18.1293 22.6943 17.9325 22.4975C16.2479 20.813 16.2479 18.0721 17.9325 16.3875C18.1293 16.1907 18.1293 15.8717 17.9325 15.6749C17.7356 15.4782 17.4166 15.4782 17.2198 15.6749C15.1423 17.7525 15.1423 21.1327 17.2198 23.2103C17.3182 23.3086 17.4472 23.3577 17.5761 23.3577Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M18.6358 21.4001C18.744 21.4001 18.8522 21.3588 18.9348 21.2762C19.0999 21.1111 19.0999 20.8434 18.9347 20.6783C18.6046 20.3483 18.4228 19.9093 18.4228 19.4424C18.4228 18.9756 18.6046 18.5367 18.9347 18.2066C19.0999 18.0415 19.0999 17.7738 18.9348 17.6087C18.7696 17.4436 18.5019 17.4436 18.3369 17.6087C17.847 18.0985 17.5772 18.7497 17.5772 19.4424C17.5772 20.1352 17.847 20.7864 18.3369 21.2763C18.4193 21.3587 18.5276 21.4001 18.6358 21.4001Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M11.1713 30.5347L11.1713 32.4539C11.1713 32.8359 10.8617 33.1455 10.4797 33.1455C10.0976 33.1455 9.78809 32.8359 9.78809 32.4539L9.78809 30.5347C9.78809 30.1527 10.0976 29.8431 10.4797 29.8431C10.8617 29.8431 11.1713 30.1527 11.1713 30.5347Z" fill="#B3DAFE"/>
|
||||||
|
<path d="M21.5342 8.79642C21.5342 7.91189 22.1118 7.11379 22.9777 6.93351C24.3924 6.63901 25.5238 6.65107 26.9209 6.93884C27.7961 7.1191 28.3858 7.92191 28.3858 8.81547V31.0421C28.3858 31.9071 27.8339 32.6929 26.9918 32.8912C25.5575 33.2289 24.3936 33.2319 22.9318 32.8895C22.0881 32.6918 21.5342 31.9055 21.5342 31.039V8.79642Z" fill="#EAF6FF"/>
|
||||||
|
<path d="M23.9952 23.3577C23.8662 23.3577 23.7372 23.3086 23.6388 23.2102C23.442 23.0133 23.442 22.6943 23.6388 22.4975C25.3234 20.813 25.3234 18.0721 23.6388 16.3875C23.442 16.1907 23.442 15.8717 23.6388 15.6749C23.8357 15.4782 24.1547 15.4782 24.3515 15.6749C26.429 17.7525 26.429 21.1327 24.3515 23.2103C24.2531 23.3086 24.1241 23.3577 23.9952 23.3577Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M22.9355 21.4001C22.8273 21.4001 22.7191 21.3588 22.6365 21.2762C22.4714 21.1111 22.4714 20.8434 22.6366 20.6783C22.9666 20.3483 23.1485 19.9093 23.1485 19.4424C23.1485 18.9756 22.9666 18.5367 22.6366 18.2066C22.4714 18.0415 22.4714 17.7738 22.6365 17.6087C22.8017 17.4436 23.0694 17.4436 23.2344 17.6087C23.7243 18.0985 23.9941 18.7497 23.9941 19.4424C23.9941 20.1352 23.7243 20.7864 23.2344 21.2763C23.1519 21.3587 23.0437 21.4001 22.9355 21.4001Z" fill="#8AC9FE"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_2351_2766" x="-1" y="0" width="21.5547" height="38" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-1"/>
|
||||||
|
<feGaussianBlur stdDeviation="1.5"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0.3 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_2351_2766"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
10
assets/icons/main_door_notifi.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg width="31" height="35" viewBox="0 0 31 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.4316 6.37335C16.8654 6.37335 16.4062 5.91425 16.4062 5.34796V3.07617C16.4062 2.51077 15.9463 2.05078 15.3809 2.05078C14.8155 2.05078 14.3555 2.51077 14.3555 3.07617V5.34796C14.3555 5.91425 13.8964 6.37335 13.3301 6.37335C12.7638 6.37335 12.3047 5.91425 12.3047 5.34796V3.07617C12.3047 1.37997 13.6847 0 15.3809 0C17.0771 0 18.457 1.37997 18.457 3.07617V5.34796C18.457 5.91425 17.9979 6.37335 17.4316 6.37335Z" fill="#FFB454"/>
|
||||||
|
<path d="M16.4062 3.07617V5.34796C16.4062 5.91425 16.8654 6.37335 17.4316 6.37335C17.9979 6.37335 18.457 5.91425 18.457 5.34796V3.07617C18.457 1.37997 17.0771 0 15.3809 0V2.05078C15.9463 2.05078 16.4062 2.51077 16.4062 3.07617Z" fill="#FF8E00"/>
|
||||||
|
<path d="M15.3809 35C12.5539 35 10.2539 32.7 10.2539 29.873C10.2539 29.3068 10.713 28.8477 11.2793 28.8477H19.4824C20.0487 28.8477 20.5078 29.3068 20.5078 29.873C20.5078 32.7 18.2079 35 15.3809 35Z" fill="#FFB454"/>
|
||||||
|
<path d="M19.4824 28.8477H15.3809V35C18.2079 35 20.5078 32.7 20.5078 29.873C20.5078 29.3068 20.0487 28.8477 19.4824 28.8477Z" fill="#FF8E00"/>
|
||||||
|
<path d="M29.7363 30.8984H1.0254C0.614562 30.8984 0.243439 30.6532 0.0822478 30.2753C-0.0789436 29.8975 0.000968566 29.4599 0.285344 29.1633C3.18706 26.1372 4.78517 22.1616 4.78517 17.9691V14.6973C4.78517 8.85479 9.5384 4.10156 15.3809 4.10156C21.2233 4.10156 25.9766 8.85479 25.9766 14.6973V17.9691C25.9766 22.1617 27.5747 26.1372 30.4765 29.1633C30.7608 29.4599 30.8407 29.8975 30.6795 30.2753C30.5184 30.6532 30.1472 30.8984 29.7363 30.8984Z" fill="#FFE278"/>
|
||||||
|
<path d="M29.7363 30.8984C30.1472 30.8984 30.5183 30.6532 30.6795 30.2753C30.8407 29.8975 30.7608 29.4599 30.4765 29.1633C27.5747 26.1372 25.9766 22.1616 25.9766 17.9691V14.6973C25.9766 8.85479 21.2233 4.10156 15.3809 4.10156V30.8984H29.7363Z" fill="#FFB454"/>
|
||||||
|
<path d="M29.7359 15.7226C29.1696 15.7226 28.7105 15.2635 28.7105 14.6972C28.7105 11.1366 27.3239 7.78912 24.8062 5.27144C24.4057 4.87099 24.4057 4.22178 24.8062 3.82133C25.2066 3.42088 25.8559 3.42088 26.2563 3.82133C29.1614 6.72633 30.7613 10.5888 30.7613 14.6972C30.7613 15.2635 30.3022 15.7226 29.7359 15.7226Z" fill="#08475E"/>
|
||||||
|
<path d="M1.02539 15.7226C0.459102 15.7226 0 15.2635 0 14.6972C0 10.5888 1.59988 6.7264 4.50495 3.82133C4.90533 3.42088 5.55461 3.42088 5.95506 3.82133C6.35551 4.22178 6.35551 4.87099 5.95506 5.27144C3.43738 7.78918 2.05078 11.1367 2.05078 14.6972C2.05078 15.2635 1.59168 15.7226 1.02539 15.7226Z" fill="#0A789B"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
18
assets/icons/main_door_reports.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.62771 22.7848L0.0206433 9.97422C-0.0455284 9.78766 0.0527038 9.58101 0.239256 9.51286C6.29643 7.33432 13.1218 5.30816 18.9563 2.81865C19.2171 2.65418 19.4217 2.81667 19.534 3.12757L26.7725 21.7858L28.7381 27.2523C28.8043 27.4388 28.7081 27.6475 28.5215 27.7137L14.6702 32.6947L9.27087 34.6366C9.08432 34.7048 8.87774 34.6065 8.80958 34.4199L4.62771 22.7848Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.46894 18.5338L6.16844 5.11738C6.13433 4.92078 6.26674 4.73423 6.46129 4.70012L22.3042 1.98393L25.9325 1.38613C26.1311 1.29986 26.454 1.40015 26.5302 1.84352L30.4493 21.4146L31.4301 27.1398C31.4642 27.3345 31.3318 27.523 31.1372 27.5571L16.6301 30.0446L10.974 31.0155C10.7795 31.0477 10.5909 30.9152 10.5568 30.7206L8.46894 18.5338Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9063 14.5779V27.4768C12.9063 27.6835 13.0748 27.854 13.2814 27.854H19.2684H28.1315H29.4051H34.624C34.8305 27.854 34.999 27.6835 34.999 27.4768V21.4166V4.67611V4.50159H31.1882C30.8212 4.50159 30.059 4.55375 30.059 3.72127L30.0509 0H13.2813C13.0748 0 12.9062 0.168505 12.9062 0.375155V3.59487V4.957V14.5779H12.9063Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.54915 7.34229C4.40308 8.0645 2.27704 8.78063 0.239256 9.51283C0.0527037 9.58105 -0.0455284 9.78763 0.0206433 9.97418L4.62771 22.7848L8.80958 34.4199C8.87781 34.6065 9.08439 34.7048 9.27087 34.6365L14.6702 32.6947L26.9732 28.2713L6.54915 7.34229Z" fill="#B5C4CF"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9076 3.59473L6.46129 4.70003C6.26674 4.73414 6.13433 4.92069 6.16844 5.11729L6.54955 7.34197L8.469 18.5337L10.5569 30.7205C10.591 30.9151 10.7795 31.0474 10.9741 31.0154L16.6301 30.0445L26.9735 28.2711L29.4064 27.8538H28.1328L12.9076 3.59473Z" fill="#D7E7EC"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.999 21.4166V4.67611V4.50159V4.44342C34.999 4.33507 34.9528 4.31702 34.7864 4.15453L30.6727 0.230712C30.4782 0.0461424 30.4461 0 30.3318 0H30.0509H13.2813C13.0748 0 12.9062 0.168505 12.9062 0.375155V27.4768C12.9062 27.6835 13.0748 27.854 13.2813 27.854H34.6239C34.8305 27.854 34.999 27.6835 34.999 27.4768V21.4166Z" fill="#EDF3F4"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9077 3.59473L11.3975 3.85353V28.8629C11.3975 29.0716 11.566 29.2401 11.7725 29.2401H17.7595H21.3216L26.9736 28.2712L29.4065 27.8539H28.1329H26.5665H19.2698H13.2828C13.0762 27.8539 12.9077 27.6834 12.9077 27.4768C12.9077 19.5167 12.9077 11.5547 12.9077 3.59473Z" fill="#B5C4CF"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.46896 18.5336L6.54951 7.3418C6.03005 7.51632 5.51257 7.69084 4.99707 7.86536L5.23578 9.25558L7.15524 20.4473L9.24518 32.6341C9.27724 32.8286 9.46578 32.961 9.66033 32.929L15.3164 31.9581L18 31.4987L26.0829 28.59L25.9867 28.4395L21.3214 29.2399L16.6302 30.0444L10.9741 31.0153C10.7796 31.0474 10.591 30.915 10.5569 30.7204L8.46896 18.5336Z" fill="#9AAFB7"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.9998 21.4166V4.67611V4.50159C34.9998 4.29897 34.9337 4.29699 34.7873 4.15453L30.6736 0.230712C30.4791 0.0461424 30.447 0 30.3326 0H30.0518H25.8779C30.9784 6.8286 31.2692 19.1377 27.4845 26.8209C27.31 27.178 27.1275 27.521 26.937 27.854H28.1323H29.4059H34.6248C34.8313 27.854 34.9998 27.6835 34.9998 27.4768V21.4166Z" fill="#D7E7EC"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.0607 3.72127C30.0607 4.55375 30.8229 4.50159 31.19 4.50159H35.0008V4.44342C35.0008 4.33507 34.9546 4.31702 34.7882 4.15453L30.6745 0.230712C30.48 0.0461424 30.4479 0 30.3336 0H30.0527L30.0607 3.72127Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.7882 4.15453L30.6745 0.230712C30.48 0.0461424 30.4479 0 30.3336 0H30.0527L30.0607 3.7212C30.0607 4.55368 30.8229 4.50152 31.19 4.50152H35.0008V4.44335C35.0008 4.33507 34.9546 4.31702 34.7882 4.15453Z" fill="#B5C4CF"/>
|
||||||
|
<path d="M31.6094 8.20367H16.5086C16.2169 8.20367 15.9805 7.96715 15.9805 7.67532C15.9805 7.3835 16.2169 7.14697 16.5086 7.14697H31.6094C31.9011 7.14697 32.1376 7.3835 32.1376 7.67532C32.1376 7.96715 31.9012 8.20367 31.6094 8.20367Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M31.6094 11.9566H16.5086C16.2169 11.9566 15.9805 11.7201 15.9805 11.4283C15.9805 11.1364 16.2169 10.8999 16.5086 10.8999H31.6094C31.9011 10.8999 32.1376 11.1364 32.1376 11.4283C32.1376 11.7201 31.9012 11.9566 31.6094 11.9566Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M31.6094 15.7105H16.5086C16.2169 15.7105 15.9805 15.474 15.9805 15.1822C15.9805 14.8903 16.2169 14.6538 16.5086 14.6538H31.6094C31.9011 14.6538 32.1376 14.8903 32.1376 15.1822C32.1376 15.474 31.9012 15.7105 31.6094 15.7105Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M31.6094 19.4634H16.5086C16.2169 19.4634 15.9805 19.2269 15.9805 18.9351C15.9805 18.6433 16.2169 18.4067 16.5086 18.4067H31.6094C31.9011 18.4067 32.1376 18.6433 32.1376 18.9351C32.1376 19.2269 31.9012 19.4634 31.6094 19.4634Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M31.6094 23.2169H16.5086C16.2169 23.2169 15.9805 22.9803 15.9805 22.6885C15.9805 22.3967 16.2169 22.1602 16.5086 22.1602H31.6094C31.9011 22.1602 32.1376 22.3967 32.1376 22.6885C32.1376 22.9803 31.9012 23.2169 31.6094 23.2169Z" fill="#9AAFB7"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
24
assets/icons/open_close_door.svg
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="30" cy="30" r="30" fill="white"/>
|
||||||
|
<g filter="url(#filter0_i_2351_2399)">
|
||||||
|
<path d="M16 16.1817C16 13.6934 17.8282 11.5608 20.3041 11.3129C24.581 10.8846 27.9929 10.9034 32.2213 11.3239C34.7091 11.5713 36.5548 13.7089 36.5548 16.209V43.7835C36.5548 46.246 34.7637 48.3665 32.3165 48.6408C28.0557 49.1183 24.5855 49.1222 20.2426 48.6376C17.7933 48.3643 16 46.2435 16 43.7791V16.1817Z" fill="#EAF6FF"/>
|
||||||
|
</g>
|
||||||
|
<path d="M33.5761 34.3577C33.7051 34.3577 33.8341 34.3086 33.9325 34.2102C34.1293 34.0133 34.1293 33.6943 33.9325 33.4975C32.2479 31.813 32.2479 29.0721 33.9325 27.3875C34.1293 27.1907 34.1293 26.8717 33.9325 26.6749C33.7356 26.4782 33.4166 26.4782 33.2198 26.6749C31.1423 28.7525 31.1423 32.1327 33.2198 34.2103C33.3182 34.3086 33.4472 34.3577 33.5761 34.3577Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M34.6358 32.4001C34.744 32.4001 34.8522 32.3588 34.9348 32.2762C35.0999 32.1111 35.0999 31.8434 34.9347 31.6783C34.6046 31.3483 34.4228 30.9093 34.4228 30.4424C34.4228 29.9756 34.6046 29.5367 34.9347 29.2066C35.0999 29.0415 35.0999 28.7738 34.9348 28.6087C34.7696 28.4436 34.5019 28.4436 34.3369 28.6087C33.847 29.0985 33.5772 29.7497 33.5772 30.4424C33.5772 31.1352 33.847 31.7864 34.3369 32.2763C34.4193 32.3587 34.5276 32.4001 34.6358 32.4001Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M27.1713 41.5347L27.1713 43.4539C27.1713 43.8359 26.8617 44.1455 26.4797 44.1455C26.0976 44.1455 25.7881 43.8359 25.7881 43.4539L25.7881 41.5347C25.7881 41.1527 26.0976 40.8431 26.4797 40.8431C26.8617 40.8431 27.1713 41.1527 27.1713 41.5347Z" fill="#B3DAFE"/>
|
||||||
|
<path d="M37.5342 19.7964C37.5342 18.9119 38.1118 18.1138 38.9777 17.9335C40.3924 17.639 41.5238 17.6511 42.9209 17.9388C43.7961 18.1191 44.3858 18.9219 44.3858 19.8155V42.0421C44.3858 42.9071 43.8339 43.6929 42.9918 43.8912C41.5575 44.2289 40.3936 44.2319 38.9318 43.8895C38.0881 43.6918 37.5342 42.9055 37.5342 42.039V19.7964Z" fill="#EAF6FF"/>
|
||||||
|
<path d="M39.9952 34.3577C39.8662 34.3577 39.7372 34.3086 39.6388 34.2102C39.442 34.0133 39.442 33.6943 39.6388 33.4975C41.3234 31.813 41.3234 29.0721 39.6388 27.3875C39.442 27.1907 39.442 26.8717 39.6388 26.6749C39.8357 26.4782 40.1547 26.4782 40.3515 26.6749C42.429 28.7525 42.429 32.1327 40.3515 34.2103C40.2531 34.3086 40.1241 34.3577 39.9952 34.3577Z" fill="#8AC9FE"/>
|
||||||
|
<path d="M38.9355 32.4001C38.8273 32.4001 38.7191 32.3588 38.6365 32.2762C38.4714 32.1111 38.4714 31.8434 38.6366 31.6783C38.9666 31.3483 39.1485 30.9093 39.1485 30.4424C39.1485 29.9756 38.9666 29.5367 38.6366 29.2066C38.4714 29.0415 38.4714 28.7738 38.6365 28.6087C38.8017 28.4436 39.0694 28.4436 39.2344 28.6087C39.7243 29.0985 39.9941 29.7497 39.9941 30.4424C39.9941 31.1352 39.7243 31.7864 39.2344 32.2763C39.1519 32.3587 39.0437 32.4001 38.9355 32.4001Z" fill="#8AC9FE"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_i_2351_2399" x="15" y="11" width="21.5547" height="38" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dx="-1"/>
|
||||||
|
<feGaussianBlur stdDeviation="1.5"/>
|
||||||
|
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0.3 0"/>
|
||||||
|
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_2351_2399"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
19
assets/icons/open_close_records.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="30" cy="30" r="30" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.6277 34.7848L12.0206 21.9742C11.9545 21.7877 12.0527 21.581 12.2393 21.5129C18.2964 19.3343 25.1218 17.3082 30.9563 14.8187C31.2171 14.6542 31.4217 14.8167 31.534 15.1276L38.7725 33.7858L40.7381 39.2523C40.8043 39.4388 40.7081 39.6475 40.5215 39.7137L26.6702 44.6947L21.2709 46.6366C21.0843 46.7048 20.8777 46.6065 20.8096 46.4199L16.6277 34.7848Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.4689 30.5338L18.1684 17.1174C18.1343 16.9208 18.2667 16.7342 18.4613 16.7001L34.3042 13.9839L37.9325 13.3861C38.1311 13.2999 38.454 13.4001 38.5302 13.8435L42.4493 33.4146L43.4301 39.1398C43.4642 39.3345 43.3318 39.523 43.1372 39.5571L28.6301 42.0446L22.974 43.0155C22.7795 43.0477 22.5909 42.9152 22.5568 42.7206L20.4689 30.5338Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9063 26.5779V39.4768C24.9063 39.6835 25.0748 39.854 25.2814 39.854H31.2684H40.1315H41.4051H46.624C46.8305 39.854 46.999 39.6835 46.999 39.4768V33.4166V16.6761V16.5016H43.1882C42.8212 16.5016 42.059 16.5537 42.059 15.7213L42.0509 12H25.2813C25.0748 12 24.9062 12.1685 24.9062 12.3752V15.5949V16.957V26.5779H24.9063Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.5492 19.3423C16.4031 20.0645 14.277 20.7806 12.2393 21.5128C12.0527 21.581 11.9545 21.7876 12.0206 21.9742L16.6277 34.7848L20.8096 46.4199C20.8778 46.6065 21.0844 46.7048 21.2709 46.6365L26.6702 44.6947L38.9732 40.2713L18.5492 19.3423Z" fill="#B5C4CF"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9076 15.5947L18.4613 16.7C18.2667 16.7341 18.1343 16.9207 18.1684 17.1173L18.5495 19.342L20.469 30.5337L22.5569 42.7205C22.591 42.9151 22.7795 43.0474 22.9741 43.0154L28.6301 42.0445L38.9735 40.2711L41.4064 39.8538H40.1328L24.9076 15.5947Z" fill="#D7E7EC"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.999 33.4166V16.6761V16.5016V16.4434C46.999 16.3351 46.9528 16.317 46.7864 16.1545L42.6727 12.2307C42.4782 12.0461 42.4461 12 42.3318 12H42.0509H25.2813C25.0748 12 24.9062 12.1685 24.9062 12.3752V39.4768C24.9062 39.6835 25.0748 39.854 25.2813 39.854H46.6239C46.8305 39.854 46.999 39.6835 46.999 39.4768V33.4166Z" fill="#EDF3F4"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.9077 15.5947L23.3975 15.8535V40.8629C23.3975 41.0716 23.566 41.2401 23.7725 41.2401H29.7595H33.3216L38.9736 40.2712L41.4065 39.8539H40.1329H38.5665H31.2698H25.2828C25.0762 39.8539 24.9077 39.6834 24.9077 39.4768C24.9077 31.5167 24.9077 23.5547 24.9077 15.5947Z" fill="#B5C4CF"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.469 30.5336L18.5495 19.3418C18.03 19.5163 17.5126 19.6908 16.9971 19.8654L17.2358 21.2556L19.1552 32.4473L21.2452 44.6341C21.2772 44.8286 21.4658 44.961 21.6603 44.929L27.3164 43.9581L30 43.4987L38.0829 40.59L37.9867 40.4395L33.3214 41.2399L28.6302 42.0444L22.9741 43.0153C22.7796 43.0474 22.591 42.915 22.5569 42.7204L20.469 30.5336Z" fill="#9AAFB7"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.9998 33.4166V16.6761V16.5016C46.9998 16.299 46.9337 16.297 46.7873 16.1545L42.6736 12.2307C42.4791 12.0461 42.447 12 42.3326 12H42.0518H37.8779C42.9784 18.8286 43.2692 31.1377 39.4845 38.8209C39.31 39.178 39.1275 39.521 38.937 39.854H40.1323H41.4059H46.6248C46.8313 39.854 46.9998 39.6835 46.9998 39.4768V33.4166Z" fill="#D7E7EC"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.0607 15.7213C42.0607 16.5537 42.8229 16.5016 43.19 16.5016H47.0008V16.4434C47.0008 16.3351 46.9546 16.317 46.7882 16.1545L42.6745 12.2307C42.48 12.0461 42.4479 12 42.3336 12H42.0527L42.0607 15.7213Z" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M46.7882 16.1545L42.6745 12.2307C42.48 12.0461 42.4479 12 42.3336 12H42.0527L42.0607 15.7212C42.0607 16.5537 42.8229 16.5015 43.19 16.5015H47.0008V16.4433C47.0008 16.3351 46.9546 16.317 46.7882 16.1545Z" fill="#B5C4CF"/>
|
||||||
|
<path d="M43.6094 20.2037H28.5086C28.2169 20.2037 27.9805 19.9671 27.9805 19.6753C27.9805 19.3835 28.2169 19.147 28.5086 19.147H43.6094C43.9011 19.147 44.1376 19.3835 44.1376 19.6753C44.1376 19.9671 43.9012 20.2037 43.6094 20.2037Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M43.6094 23.9566H28.5086C28.2169 23.9566 27.9805 23.7201 27.9805 23.4283C27.9805 23.1364 28.2169 22.8999 28.5086 22.8999H43.6094C43.9011 22.8999 44.1376 23.1364 44.1376 23.4283C44.1376 23.7201 43.9012 23.9566 43.6094 23.9566Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M43.6094 27.7105H28.5086C28.2169 27.7105 27.9805 27.474 27.9805 27.1822C27.9805 26.8903 28.2169 26.6538 28.5086 26.6538H43.6094C43.9011 26.6538 44.1376 26.8903 44.1376 27.1822C44.1376 27.474 43.9012 27.7105 43.6094 27.7105Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M43.6094 31.4634H28.5086C28.2169 31.4634 27.9805 31.2269 27.9805 30.9351C27.9805 30.6433 28.2169 30.4067 28.5086 30.4067H43.6094C43.9011 30.4067 44.1376 30.6433 44.1376 30.9351C44.1376 31.2269 43.9012 31.4634 43.6094 31.4634Z" fill="#9AAFB7"/>
|
||||||
|
<path d="M43.6094 35.2169H28.5086C28.2169 35.2169 27.9805 34.9803 27.9805 34.6885C27.9805 34.3967 28.2169 34.1602 28.5086 34.1602H43.6094C43.9011 34.1602 44.1376 34.3967 44.1376 34.6885C44.1376 34.9803 43.9012 35.2169 43.6094 35.2169Z" fill="#9AAFB7"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
5
assets/icons/unlock_ic.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="30" height="40" viewBox="0 0 30 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M15.0004 0C9.02035 0.00734375 4.17434 4.85328 4.16699 10.8334V17.5C4.16699 17.9602 4.54012 18.3334 5.00035 18.3334H8.33371C8.79395 18.3334 9.16707 17.9602 9.16707 17.5V10.8334C9.16699 7.61164 11.7787 5 15.0004 5C18.222 5 20.8337 7.61164 20.8337 10.8334V11.6666C20.8337 12.1269 21.2068 12.5 21.6671 12.5H25.0004C25.4606 12.5 25.8337 12.1269 25.8337 11.6666V10.8334C25.8264 4.85328 20.9804 0.00734375 15.0004 0Z" fill="#455A64"/>
|
||||||
|
<path d="M5.00014 16.6666H25.0001C27.3013 16.6666 29.1668 18.5321 29.1668 20.8333V35.8333C29.1668 38.1345 27.3013 40 25.0001 40H5.00014C2.69897 40 0.833496 38.1345 0.833496 35.8333V20.8333C0.833496 18.5321 2.69897 16.6666 5.00014 16.6666Z" fill="#2F66D3"/>
|
||||||
|
<path d="M19.1668 25.8333C19.1765 23.5321 17.3189 21.6588 15.0178 21.6491C12.7166 21.6394 10.8432 23.497 10.8335 25.7981C10.8268 27.3888 11.7263 28.8444 13.1518 29.5499L12.5085 34.0499C12.444 34.5056 12.7612 34.9273 13.2169 34.9917C13.2555 34.9972 13.2945 34.9999 13.3335 34.9999H16.6668C17.127 35.0046 17.5039 34.6353 17.5085 34.1751C17.5089 34.1327 17.5062 34.0903 17.5001 34.0483L16.8567 29.5483C18.2649 28.8436 19.1577 27.4078 19.1668 25.8333Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
22
assets/icons/water_heater.svg
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<svg width="25" height="36" viewBox="0 0 25 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M9.67036 31.6755C9.37913 31.6755 9.14303 31.9116 9.14303 32.2029V33.5343C9.14303 34.3122 8.5101 34.9452 7.73219 34.9452H6.41014C6.11891 34.9452 5.88281 35.1813 5.88281 35.4725C5.88281 35.7637 6.11891 35.9998 6.41014 35.9998H7.73219C9.09163 35.9998 10.1977 34.8938 10.1977 33.5343V32.2029C10.1977 31.9116 9.96158 31.6755 9.67036 31.6755Z" fill="#72BBFF"/>
|
||||||
|
<path d="M10.6115 29.458H8.7285C8.43727 29.458 8.20117 29.6941 8.20117 29.9853V31.2785C8.20117 32.0885 8.86012 32.7473 9.67002 32.7473C10.4799 32.7473 11.1389 32.0884 11.1389 31.2785V29.9853C11.1389 29.6941 10.9028 29.458 10.6115 29.458Z" fill="#6B717D"/>
|
||||||
|
<path d="M14.4785 31.6755C14.7697 31.6755 15.0058 31.9116 15.0058 32.2029V33.5343C15.0058 34.3122 15.6388 34.9452 16.4167 34.9452H17.7387C18.0299 34.9452 18.266 35.1813 18.266 35.4725C18.266 35.7637 18.0299 35.9998 17.7387 35.9998H16.4167C15.0572 35.9998 13.9512 34.8938 13.9512 33.5343V32.2029C13.9512 31.9116 14.1873 31.6755 14.4785 31.6755Z" fill="#FF6C6C"/>
|
||||||
|
<path d="M13.5371 29.4583H15.4201C15.7114 29.4583 15.9475 29.6944 15.9475 29.9856V31.2787C15.9475 32.0887 15.2885 32.7476 14.4786 32.7476C13.6687 32.7476 13.0098 32.0886 13.0098 31.2787V29.9856C13.0098 29.6944 13.2459 29.4583 13.5371 29.4583Z" fill="#6B717D"/>
|
||||||
|
<path d="M2.14594 3.29248H0.527327C0.236102 3.29248 0 3.52858 0 3.81981C0 4.11103 0.236102 4.34714 0.527327 4.34714H2.14594C2.43717 4.34714 2.67327 4.11103 2.67327 3.81981C2.67327 3.52858 2.43717 3.29248 2.14594 3.29248Z" fill="#6B717D"/>
|
||||||
|
<path d="M2.14594 5.75342H0.527327C0.236102 5.75342 0 5.98952 0 6.28075C0 6.57197 0.236102 6.80807 0.527327 6.80807H2.14594C2.43717 6.80807 2.67327 6.57197 2.67327 6.28075C2.67327 5.98952 2.43717 5.75342 2.14594 5.75342Z" fill="#6B717D"/>
|
||||||
|
<path d="M23.6186 17.7485H22C21.7088 17.7485 21.4727 17.9846 21.4727 18.2759C21.4727 18.5671 21.7088 18.8032 22 18.8032H23.6186C23.9098 18.8032 24.1459 18.5671 24.1459 18.2759C24.1459 17.9846 23.9098 17.7485 23.6186 17.7485Z" fill="#6B717D"/>
|
||||||
|
<path d="M19.5902 27.5019V29.9663C19.5902 30.2574 19.354 30.4936 19.0629 30.4936H5.08592C4.79484 30.4936 4.55859 30.2574 4.55859 29.9663V27.5019C4.55859 27.2109 4.79484 26.9746 5.08592 26.9746H19.0629C19.354 26.9746 19.5902 27.2109 19.5902 27.5019Z" fill="#D6EAEC"/>
|
||||||
|
<path d="M19.5898 27.5019V29.9663C19.5898 30.2574 19.3535 30.4936 19.0624 30.4936H16.9531C17.2442 30.4936 17.4805 30.2574 17.4805 29.9663V27.5019C17.4805 27.2109 17.2442 26.9746 16.9531 26.9746H19.0624C19.3535 26.9746 19.5898 27.2109 19.5898 27.5019Z" fill="#B5D9DD"/>
|
||||||
|
<path d="M22.5287 1.66214V22.3242L20.4194 22.8994L12.0736 25.1767L1.61914 22.3242V1.66214C1.61914 0.745289 2.36443 0 3.28128 0H20.8666C21.7827 0 22.5287 0.745289 22.5287 1.66214Z" fill="#D6EAEC"/>
|
||||||
|
<path d="M22.5273 1.66214V22.3242L20.418 22.8994V1.66214C20.418 0.745289 19.672 0 18.7559 0H20.8652C21.7813 0 22.5273 0.745289 22.5273 1.66214Z" fill="#B5D9DD"/>
|
||||||
|
<path d="M22.5287 22.3242V26.3671C22.5287 27.2839 21.7827 28.0292 20.8666 28.0292H3.28128C2.36443 28.0292 1.61914 27.2839 1.61914 26.3671V22.3242H22.5287Z" fill="#6B717D"/>
|
||||||
|
<path d="M22.5273 22.3242V26.3671C22.5273 27.2839 21.7813 28.0292 20.8652 28.0292H18.7559C19.672 28.0292 20.418 27.2839 20.418 26.3671V22.3242H22.5273Z" fill="#47505E"/>
|
||||||
|
<path d="M12.43 13.9283C12.2285 13.744 11.9196 13.744 11.7181 13.9283C11.6376 14.0019 9.74805 15.7519 9.74805 17.6083C9.74805 18.8909 10.7915 19.9344 12.0741 19.9344C13.3567 19.9344 14.4001 18.8909 14.4001 17.6083C14.4 15.7519 12.5104 14.0019 12.43 13.9283Z" fill="#72BBFF"/>
|
||||||
|
<path d="M16.1576 8.85955C17.0908 6.60807 16.0222 4.02635 13.7707 3.0931C11.5192 2.15986 8.93748 3.2285 8.00424 5.47998C7.071 7.73146 8.13964 10.3132 10.3911 11.2464C12.6426 12.1797 15.2243 11.111 16.1576 8.85955Z" fill="#B5D9DD"/>
|
||||||
|
<path d="M14.7283 7.16982C14.7283 9.02109 13.9254 10.5271 12.0741 10.5271C11.7957 10.5271 11.525 10.4927 11.2655 10.428C9.80379 10.0659 8.7168 8.74266 8.7168 7.16982C8.7168 5.59698 9.80379 4.27374 11.2655 3.91164C11.525 3.84695 11.7957 3.8125 12.0741 3.8125C13.9254 3.8125 14.7283 5.31855 14.7283 7.16982Z" fill="white"/>
|
||||||
|
<path d="M15.4315 7.16982C15.4315 9.02109 13.9255 10.5271 12.0742 10.5271C11.7958 10.5271 11.5251 10.4927 11.2656 10.428C12.7274 10.0659 13.8144 8.74266 13.8144 7.16982C13.8144 5.59698 12.7274 4.27374 11.2656 3.91164C11.5251 3.84695 11.7958 3.8125 12.0742 3.8125C13.9255 3.8125 15.4315 5.31855 15.4315 7.16982Z" fill="#D6EAEC"/>
|
||||||
|
<path d="M12.4478 6.79706C12.2418 6.59105 11.908 6.59112 11.7021 6.79706L10.5783 7.92094C10.3723 8.12688 10.3723 8.4608 10.5783 8.66667C10.6812 8.76961 10.8162 8.82115 10.9512 8.82115C11.0861 8.82115 11.2211 8.76968 11.324 8.66667L12.4478 7.5428C12.6538 7.33685 12.6538 7.00294 12.4478 6.79706Z" fill="#FF6C6C"/>
|
||||||
|
<path d="M5.55469 25.7041C5.84593 25.7041 6.08203 25.468 6.08203 25.1768C6.08203 24.8855 5.84593 24.6494 5.55469 24.6494C5.26344 24.6494 5.02734 24.8855 5.02734 25.1768C5.02734 25.468 5.26344 25.7041 5.55469 25.7041Z" fill="#47505E"/>
|
||||||
|
<path d="M18.5918 25.7041C18.883 25.7041 19.1191 25.468 19.1191 25.1768C19.1191 24.8855 18.883 24.6494 18.5918 24.6494C18.3006 24.6494 18.0645 24.8855 18.0645 25.1768C18.0645 25.468 18.3006 25.7041 18.5918 25.7041Z" fill="#47505E"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.1 KiB |
17
assets/images/curtain.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<svg width="36" height="35" viewBox="0 0 36 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.768 0.417969H0.825168C0.825168 0.417969 0.398459 11.6714 1.87939 17.9772V17.98H1.8766C0.705244 20.8163 0.515595 34.4599 0.981349 34.6189C1.76504 34.8866 3.07864 35.3077 4.21932 34.5045C4.77432 35.1292 7.28717 35.238 7.40151 34.393C9.0693 35.1794 10.2909 34.895 10.2602 34.3957C9.71355 25.4488 7.11146 19.0091 7.11146 19.0091C7.11146 19.0091 16.6999 12.2877 17.768 0.417969Z" fill="#448CF2"/>
|
||||||
|
<path d="M33.66 17.98H33.6572V17.9772C35.1381 11.6714 34.7114 0.417969 34.7114 0.417969H17.7686C18.8367 12.2877 28.4251 19.0091 28.4251 19.0091C28.4251 19.0091 25.823 25.4488 25.2764 34.3957C25.2457 34.895 26.4673 35.1794 28.1351 34.393C28.2494 35.238 30.7623 35.1292 31.3173 34.5045C32.458 35.3077 33.7716 34.8866 34.5552 34.6189C35.021 34.4599 34.8314 20.8163 33.66 17.98Z" fill="#448CF2"/>
|
||||||
|
<path d="M6.84854 27.4424C6.7382 27.4534 6.63674 27.5078 6.56646 27.5935C6.49617 27.6793 6.46281 27.7894 6.47371 27.8998C6.85579 31.7307 6.98129 34.3858 6.98269 34.4123C6.98764 34.5199 7.0339 34.6214 7.11182 34.6957C7.18974 34.7701 7.29334 34.8115 7.40103 34.8114H7.42055C7.47546 34.8089 7.52934 34.7956 7.5791 34.7722C7.62887 34.7489 7.67355 34.716 7.71058 34.6754C7.74762 34.6348 7.77629 34.5872 7.79496 34.5355C7.81363 34.4838 7.82192 34.429 7.81937 34.3741C7.81937 34.3476 7.69164 31.6718 7.30732 27.8178C7.30235 27.7628 7.28647 27.7094 7.26061 27.6606C7.23475 27.6119 7.19943 27.5688 7.15671 27.5338C7.114 27.4989 7.06474 27.4728 7.01183 27.4571C6.95892 27.4414 6.90341 27.4364 6.84854 27.4424Z" fill="#2F66D3"/>
|
||||||
|
<path d="M7.62658 18.5951C7.46284 18.6037 7.29874 18.6024 7.13516 18.5912C6.90926 18.579 6.64012 18.5522 6.34171 18.5143C6.32694 18.5126 6.31292 18.5069 6.30125 18.4977C6.28959 18.4885 6.28075 18.4762 6.27575 18.4622C6.27075 18.4482 6.26978 18.4331 6.27296 18.4186C6.27614 18.404 6.28334 18.3907 6.29374 18.3801C6.68022 17.9908 7.04563 17.5812 7.3884 17.153C7.42875 17.1037 7.45704 17.0457 7.47101 16.9836C7.48499 16.9215 7.48428 16.857 7.46892 16.7952C7.45356 16.7334 7.42399 16.6761 7.38256 16.6278C7.34112 16.5794 7.28897 16.5414 7.23027 16.5168L7.22246 16.5137C7.13959 16.4794 7.04788 16.4726 6.96086 16.4943C6.87383 16.5161 6.7961 16.5652 6.73913 16.6345C6.2991 17.1895 5.81303 17.7063 5.28609 18.1796C5.23302 18.2252 5.17036 18.2582 5.10276 18.2763C5.03517 18.2943 4.96437 18.2969 4.89564 18.2839C4.68674 18.2451 4.47311 18.203 4.2578 18.1584C4.24006 18.1548 4.22333 18.1474 4.20888 18.1365C4.19442 18.1256 4.1826 18.1116 4.1743 18.0955C4.16599 18.0794 4.16141 18.0617 4.1609 18.0436C4.16039 18.0255 4.16396 18.0076 4.17135 17.991C4.75396 16.6822 5.38342 14.1217 5.71921 12.0104C5.73663 11.9008 5.70981 11.7888 5.64464 11.699C5.57947 11.6092 5.48129 11.549 5.37171 11.5316C5.26213 11.5141 5.15011 11.541 5.06031 11.6061C4.9705 11.6713 4.91027 11.7695 4.89285 11.8791C4.49319 14.3913 3.82663 16.7726 3.36645 17.7512C3.34087 17.8046 3.29754 17.8474 3.24388 17.8724C3.19021 17.8974 3.12953 17.9029 3.07222 17.8881C2.78831 17.8167 2.22717 17.6182 1.83532 17.4762C1.78303 17.4573 1.7275 17.449 1.67196 17.4517C1.61642 17.4544 1.56198 17.4682 1.5118 17.4921C1.46161 17.5161 1.41669 17.5497 1.37963 17.5912C1.34258 17.6327 1.31413 17.6811 1.29594 17.7336L1.29259 17.7437C1.25559 17.8482 1.26149 17.9632 1.309 18.0634C1.3565 18.1637 1.44175 18.241 1.54611 18.2786C1.9572 18.4261 2.57802 18.628 2.87197 18.7017C2.92612 18.7145 2.9746 18.7447 3.01008 18.7875C3.04556 18.8304 3.0661 18.8837 3.06859 18.9393C3.09648 19.7542 3.05688 20.7602 3.02369 21.5986C3.00054 22.182 2.98046 22.6862 2.99022 22.9841C2.9937 23.0927 3.03931 23.1957 3.11739 23.2713C3.19547 23.3469 3.2999 23.3891 3.40857 23.3891C3.43423 23.3891 3.4557 23.3891 3.52013 23.3428C3.61429 23.2732 3.69082 23.1825 3.74357 23.078C3.79632 22.9734 3.82381 22.858 3.82384 22.7409C3.82607 22.462 3.84169 22.0688 3.85898 21.6301C3.8905 20.838 3.92675 19.9009 3.90835 19.0907C3.90806 19.0724 3.91193 19.0543 3.91967 19.0377C3.92741 19.0212 3.93882 19.0066 3.95303 18.995C3.96724 18.9835 3.98388 18.9754 4.0017 18.9712C4.01952 18.9671 4.03804 18.967 4.05588 18.9711C4.24748 19.011 4.43769 19.0489 4.62511 19.0843C4.70909 19.1002 4.78703 19.139 4.85033 19.1965C4.91362 19.2539 4.9598 19.3277 4.98377 19.4098C5.53161 21.2517 5.89466 23.1436 6.06755 25.0574C6.07779 25.1606 6.126 25.2564 6.20284 25.326C6.27968 25.3957 6.37966 25.4344 6.48338 25.4345C6.52761 25.4356 6.57146 25.426 6.61121 25.4066C6.65096 25.3872 6.68546 25.3585 6.7118 25.3229C6.77645 25.2473 6.8247 25.159 6.8535 25.0638C6.88231 24.9686 6.89104 24.8684 6.87914 24.7696C6.70414 22.9554 6.36102 21.1614 5.8542 19.4106C5.85074 19.3974 5.85055 19.3836 5.85366 19.3703C5.85678 19.357 5.86309 19.3447 5.87206 19.3344C5.88102 19.3241 5.89236 19.3162 5.9051 19.3113C5.91783 19.3064 5.93157 19.3047 5.94512 19.3063C6.38298 19.3682 6.7712 19.4098 7.08859 19.4274C7.22497 19.4343 7.34266 19.438 7.44585 19.438C7.52534 19.438 7.59618 19.4357 7.6606 19.4315C7.75879 19.4277 7.85279 19.3907 7.92725 19.3266C8.00171 19.2625 8.05223 19.175 8.07058 19.0785C8.08056 19.0161 8.07629 18.9523 8.05811 18.8918C8.03993 18.8314 8.00829 18.7758 7.96557 18.7293C7.92286 18.6828 7.87017 18.6466 7.81146 18.6233C7.75275 18.6001 7.68954 18.5905 7.62658 18.5951Z" fill="#2F66D3"/>
|
||||||
|
<path d="M4.50657 31.1913L4.49792 30.8008C4.49671 30.7459 4.48469 30.6917 4.46255 30.6414C4.44041 30.5912 4.40858 30.5457 4.36888 30.5077C4.32918 30.4697 4.28238 30.4399 4.23117 30.42C4.17995 30.4001 4.12531 30.3905 4.07037 30.3917C4.01544 30.3929 3.96127 30.4049 3.91098 30.4271C3.86069 30.4492 3.81525 30.481 3.77726 30.5207C3.73926 30.5604 3.70946 30.6072 3.68956 30.6585C3.66965 30.7097 3.66003 30.7643 3.66123 30.8192L3.66988 31.2097C3.71478 33.3098 3.74797 34.3542 3.81379 34.6105C3.83656 34.6992 3.88832 34.7777 3.96084 34.8335C4.03336 34.8894 4.12248 34.9194 4.214 34.9187C4.24753 34.9187 4.28094 34.9147 4.31357 34.907C4.42056 34.8803 4.5129 34.8129 4.57102 34.7191C4.62913 34.6254 4.64843 34.5127 4.62482 34.405C4.57015 34.1476 4.5286 32.2232 4.50657 31.1913Z" fill="#2F66D3"/>
|
||||||
|
<path d="M3.56385 24.9803C3.4529 24.9803 3.34649 25.0244 3.26804 25.1029C3.18958 25.1813 3.14551 25.2877 3.14551 25.3987C3.14551 25.9565 3.42859 28.2657 3.42719 28.9964C3.42719 29.1074 3.47127 29.2138 3.54972 29.2922C3.62818 29.3707 3.73458 29.4148 3.84553 29.4148C3.95649 29.4148 4.06289 29.3707 4.14135 29.2922C4.2198 29.2138 4.26388 29.1074 4.26388 28.9964C4.26527 28.2155 4.01733 25.9598 3.98219 25.3987C3.97964 25.2885 3.93475 25.1836 3.85684 25.1057C3.77893 25.0278 3.674 24.9829 3.56385 24.9803Z" fill="#2F66D3"/>
|
||||||
|
<path d="M6.19932 5.48148C6.14483 5.4736 6.08931 5.47655 6.03596 5.49017C5.98261 5.50379 5.93248 5.52781 5.88843 5.56084C5.84438 5.59388 5.80729 5.63528 5.77928 5.68269C5.75127 5.73009 5.73289 5.78255 5.7252 5.83707C5.69424 6.05628 5.48925 9.05831 5.2745 9.98145C5.24943 10.0895 5.26828 10.203 5.32691 10.2972C5.38553 10.3913 5.47914 10.4583 5.58714 10.4835C5.61832 10.4908 5.65023 10.4944 5.68225 10.4943C5.77667 10.4943 5.86829 10.4623 5.94222 10.4035C6.01615 10.3448 6.06803 10.2628 6.08943 10.1708C6.31924 9.18214 6.52702 6.1433 6.55351 5.95393C6.56897 5.84434 6.54038 5.7331 6.474 5.64455C6.40761 5.556 6.30884 5.49737 6.19932 5.48148Z" fill="#2F66D3"/>
|
||||||
|
<path d="M28.6861 27.4424C28.7965 27.4534 28.8979 27.5078 28.9682 27.5935C29.0385 27.6793 29.0719 27.7894 29.061 27.8998C28.6789 31.7307 28.5534 34.3858 28.552 34.4123C28.547 34.5199 28.5008 34.6214 28.4228 34.6957C28.3449 34.7701 28.2413 34.8115 28.1336 34.8114H28.1141C28.0592 34.8089 28.0053 34.7956 27.9556 34.7722C27.9058 34.7489 27.8611 34.716 27.8241 34.6754C27.787 34.6348 27.7584 34.5872 27.7397 34.5355C27.721 34.4838 27.7127 34.429 27.7153 34.3741C27.7153 34.3476 27.843 31.6718 28.2273 27.8178C28.2323 27.7628 28.2482 27.7094 28.2741 27.6606C28.2999 27.6119 28.3352 27.5688 28.378 27.5338C28.4207 27.4989 28.4699 27.4728 28.5228 27.4571C28.5758 27.4414 28.6313 27.4364 28.6861 27.4424Z" fill="#2F66D3"/>
|
||||||
|
<path d="M31.0294 31.1913L31.0381 30.8008C31.0393 30.7459 31.0513 30.6917 31.0735 30.6414C31.0956 30.5912 31.1274 30.5457 31.1671 30.5077C31.2068 30.4697 31.2536 30.4399 31.3048 30.42C31.3561 30.4001 31.4107 30.3905 31.4656 30.3917C31.5206 30.3929 31.5747 30.4049 31.625 30.4271C31.6753 30.4492 31.7208 30.481 31.7588 30.5207C31.7967 30.5604 31.8265 30.6072 31.8465 30.6585C31.8664 30.7097 31.876 30.7643 31.8748 30.8192L31.8661 31.2097C31.8212 33.3098 31.788 34.3542 31.7222 34.6105C31.6995 34.6992 31.6477 34.7777 31.5752 34.8335C31.5026 34.8894 31.4135 34.9194 31.322 34.9187C31.2885 34.9187 31.2551 34.9147 31.2224 34.907C31.1155 34.8803 31.0231 34.8129 30.965 34.7191C30.9069 34.6254 30.8876 34.5127 30.9112 34.405C30.9659 34.1476 31.0074 32.2232 31.0294 31.1913Z" fill="#2F66D3"/>
|
||||||
|
<path d="M31.9725 24.9803C32.0834 24.9803 32.1898 25.0244 32.2683 25.1029C32.3468 25.1813 32.3908 25.2877 32.3908 25.3987C32.3908 25.9565 32.1078 28.2657 32.1091 28.9964C32.1091 29.1074 32.0651 29.2138 31.9866 29.2922C31.9082 29.3707 31.8018 29.4148 31.6908 29.4148C31.5799 29.4148 31.4734 29.3707 31.395 29.2922C31.3165 29.2138 31.2725 29.1074 31.2725 28.9964C31.2725 28.2155 31.519 25.9598 31.5541 25.3987C31.5567 25.2885 31.6016 25.1836 31.6795 25.1057C31.7574 25.0278 31.8623 24.9829 31.9725 24.9803Z" fill="#2F66D3"/>
|
||||||
|
<path d="M29.3362 5.48141C29.4461 5.46589 29.5576 5.49464 29.6462 5.56132C29.7349 5.628 29.7934 5.72717 29.809 5.837C29.8399 6.05621 30.0449 9.05824 30.2597 9.98138C30.2847 10.0894 30.2659 10.203 30.2072 10.2971C30.1486 10.3912 30.055 10.4582 29.947 10.4834C29.9158 10.4907 29.8839 10.4943 29.8519 10.4943C29.7575 10.4942 29.6659 10.4622 29.5919 10.4035C29.518 10.3447 29.4661 10.2627 29.4447 10.1707C29.2149 9.18207 29.0071 6.14323 28.9806 5.95386C28.9652 5.84406 28.994 5.73263 29.0607 5.64404C29.1273 5.55546 29.2265 5.49696 29.3362 5.48141Z" fill="#2F66D3"/>
|
||||||
|
<path d="M10.6447 10.2047C10.539 10.1709 10.4242 10.1804 10.3256 10.2312C10.2269 10.282 10.1525 10.3699 10.1187 10.4756C9.63749 11.9296 8.98265 13.3202 8.16836 14.6174C8.10986 14.7117 8.09119 14.8253 8.11648 14.9334C8.14177 15.0414 8.20894 15.135 8.30321 15.1935C8.39748 15.252 8.51113 15.2707 8.61917 15.2454C8.7272 15.2201 8.82076 15.1529 8.87927 15.0586C9.72954 13.7033 10.4131 12.2502 10.9152 10.731C10.932 10.6787 10.9383 10.6236 10.9338 10.5688C10.9293 10.5141 10.9141 10.4607 10.889 10.4118C10.8638 10.363 10.8293 10.3195 10.7874 10.284C10.7455 10.2485 10.697 10.2215 10.6447 10.2047Z" fill="#2F66D3"/>
|
||||||
|
<path d="M27.9102 18.5951C28.0739 18.6037 28.238 18.6024 28.4016 18.5912C28.6275 18.579 28.8966 18.5522 29.1951 18.5143C29.2098 18.5126 29.2239 18.5069 29.2355 18.4977C29.2472 18.4885 29.256 18.4762 29.261 18.4622C29.266 18.4482 29.267 18.4331 29.2638 18.4186C29.2606 18.404 29.2534 18.3907 29.243 18.3801C28.8566 17.9908 28.4911 17.5812 28.1484 17.153C28.108 17.1037 28.0797 17.0457 28.0658 16.9836C28.0518 16.9215 28.0525 16.857 28.0679 16.7952C28.0832 16.7334 28.1128 16.6761 28.1542 16.6278C28.1957 16.5794 28.2478 16.5414 28.3065 16.5168L28.3143 16.5137C28.3972 16.4794 28.4889 16.4726 28.5759 16.4943C28.6629 16.5161 28.7407 16.5652 28.7976 16.6345C29.2377 17.1895 29.7237 17.7063 30.2507 18.1796C30.3038 18.2252 30.3664 18.2582 30.434 18.2763C30.5016 18.2943 30.5724 18.2969 30.6411 18.2839C30.85 18.2451 31.0637 18.203 31.279 18.1584C31.2967 18.1548 31.3134 18.1474 31.3279 18.1365C31.3424 18.1256 31.3542 18.1116 31.3625 18.0955C31.3708 18.0794 31.3754 18.0617 31.3759 18.0436C31.3764 18.0255 31.3728 18.0076 31.3654 17.991C30.7828 16.6822 30.1534 14.1217 29.8176 12.0104C29.8001 11.9008 29.827 11.7888 29.8921 11.699C29.9573 11.6092 30.0555 11.549 30.1651 11.5316C30.2746 11.5141 30.3867 11.541 30.4765 11.6061C30.5663 11.6713 30.6265 11.7695 30.6439 11.8791C31.0436 14.3913 31.7101 16.7726 32.1703 17.7512C32.196 17.8049 32.2395 17.8479 32.2935 17.8729C32.3474 17.8979 32.4084 17.9033 32.4659 17.8881C32.7499 17.8167 33.311 17.6182 33.7028 17.4762C33.7551 17.4573 33.8107 17.449 33.8662 17.4517C33.9217 17.4544 33.9762 17.4682 34.0264 17.4921C34.0766 17.5161 34.1215 17.5497 34.1585 17.5912C34.1956 17.6327 34.224 17.6811 34.2422 17.7336L34.2456 17.7437C34.2822 17.8481 34.2762 17.9628 34.2287 18.0627C34.1812 18.1627 34.0962 18.2399 33.9921 18.2775C33.581 18.425 32.9601 18.6269 32.6662 18.7006C32.6123 18.7137 32.5641 18.7439 32.5289 18.7868C32.4937 18.8296 32.4734 18.8828 32.471 18.9382C32.4431 19.7531 32.4827 20.7591 32.5159 21.5974C32.539 22.1809 32.5591 22.6851 32.5493 22.983C32.5459 23.0916 32.5003 23.1946 32.4222 23.2702C32.3441 23.3458 32.2397 23.388 32.131 23.3879C32.1053 23.3879 32.0839 23.3879 32.0194 23.3416C31.9249 23.2725 31.8479 23.1821 31.7947 23.0777C31.7415 22.9734 31.7135 22.858 31.7129 22.7409C31.7107 22.462 31.6951 22.0688 31.6778 21.6301C31.6463 20.838 31.61 19.9009 31.6284 19.0907C31.6287 19.0724 31.6248 19.0543 31.6171 19.0377C31.6094 19.0212 31.598 19.0066 31.5837 18.995C31.5695 18.9835 31.5529 18.9754 31.5351 18.9712C31.5173 18.9671 31.4987 18.967 31.4809 18.9711C31.2893 19.011 31.0991 19.0489 30.9117 19.0843C30.8277 19.1002 30.7497 19.139 30.6864 19.1965C30.6232 19.2539 30.577 19.3277 30.553 19.4098C30.0052 21.2517 29.6421 23.1436 29.4692 25.0574C29.459 25.1606 29.4108 25.2564 29.3339 25.326C29.2571 25.3957 29.1571 25.4344 29.0534 25.4345C29.0092 25.4356 28.9653 25.426 28.9256 25.4066C28.8858 25.3872 28.8513 25.3585 28.825 25.3229C28.7603 25.2473 28.7121 25.159 28.6833 25.0638C28.6545 24.9686 28.6457 24.8684 28.6576 24.7696C28.8326 22.9554 29.1757 21.1614 29.6826 19.4106C29.686 19.3974 29.6862 19.3836 29.6831 19.3703C29.68 19.357 29.6737 19.3447 29.6647 19.3344C29.6558 19.3241 29.6444 19.3162 29.6317 19.3113C29.6189 19.3064 29.6052 19.3047 29.5917 19.3063C29.1538 19.3682 28.7656 19.4098 28.4482 19.4274C28.3118 19.4343 28.1941 19.438 28.0909 19.438C28.0114 19.438 27.9406 19.4357 27.8762 19.4315C27.778 19.4277 27.684 19.3907 27.6095 19.3266C27.5351 19.2625 27.4845 19.175 27.4662 19.0785C27.4562 19.0161 27.4605 18.9523 27.4787 18.8918C27.4968 18.8314 27.5285 18.7758 27.5712 18.7293C27.6139 18.6828 27.6666 18.6466 27.7253 18.6233C27.784 18.6001 27.8472 18.5905 27.9102 18.5951Z" fill="#2F66D3"/>
|
||||||
|
<path d="M24.8921 10.2047C24.9978 10.1709 25.1126 10.1804 25.2112 10.2312C25.3099 10.282 25.3843 10.3699 25.4181 10.4756C25.8993 11.9296 26.5541 13.3202 27.3684 14.6174C27.4269 14.7117 27.4456 14.8253 27.4203 14.9334C27.395 15.0414 27.3279 15.135 27.2336 15.1935C27.1393 15.252 27.0257 15.2707 26.9176 15.2454C26.8096 15.2201 26.716 15.1529 26.6575 15.0586C25.8073 13.7033 25.1237 12.2502 24.6216 10.731C24.6048 10.6787 24.5985 10.6236 24.603 10.5688C24.6075 10.5141 24.6227 10.4607 24.6478 10.4118C24.673 10.363 24.7075 10.3195 24.7494 10.284C24.7913 10.2485 24.8398 10.2215 24.8921 10.2047Z" fill="#2F66D3"/>
|
||||||
|
<path d="M34.9896 0.836685H0.418342C0.307391 0.836685 0.200984 0.79261 0.12253 0.714155C0.0440752 0.635701 0 0.529294 0 0.418343C0 0.307391 0.0440752 0.200984 0.12253 0.12253C0.200984 0.0440752 0.307391 1.6533e-09 0.418342 0H34.9896C35.1006 1.6533e-09 35.207 0.0440752 35.2854 0.12253C35.3639 0.200984 35.408 0.307391 35.408 0.418343C35.408 0.529294 35.3639 0.635701 35.2854 0.714155C35.207 0.79261 35.1006 0.836685 34.9896 0.836685Z" fill="#2F66D3"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 15 KiB |
28
assets/images/scheduling.svg
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M16.6724 33.3447C25.8803 33.3447 33.3447 25.8803 33.3447 16.6724C33.3447 7.46448 25.8803 0 16.6724 0C7.46448 0 0 7.46448 0 16.6724C0 25.8803 7.46448 33.3447 16.6724 33.3447Z" fill="#F07281"/>
|
||||||
|
<path d="M17.3767 33.3303C17.1436 33.3401 16.9084 33.345 16.6725 33.345C7.46478 33.345 0 25.8802 0 16.6725C0 7.46478 7.46478 0 16.6725 0C16.9084 0 17.1436 0.00492188 17.3767 0.0147656C8.4957 0.38377 1.40841 7.70068 1.40841 16.6725C1.40841 25.6443 8.4957 32.9613 17.3767 33.3303Z" fill="#EB5569"/>
|
||||||
|
<path d="M16.6726 31.1577C24.6725 31.1577 31.1577 24.6725 31.1577 16.6726C31.1577 8.67269 24.6725 2.1875 16.6726 2.1875C8.67269 2.1875 2.1875 8.67269 2.1875 16.6726C2.1875 24.6725 8.67269 31.1577 16.6726 31.1577Z" fill="#EAF6FF"/>
|
||||||
|
<path d="M17.7291 31.1199C17.3805 31.1453 17.0276 31.1579 16.6727 31.1579C8.67269 31.1579 2.1875 24.6727 2.1875 16.6727C2.1875 8.67269 8.67275 2.1875 16.6728 2.1875C17.0277 2.1875 17.3805 2.20015 17.7291 2.22551C10.2221 2.76644 4.30022 9.02768 4.30022 16.6727C4.30022 24.3178 10.2221 30.5791 17.7291 31.1199Z" fill="#D8ECFE"/>
|
||||||
|
<path d="M15.7672 8.23957L16.4138 7.82326C16.5715 7.72167 16.7741 7.72167 16.9319 7.82326L17.5785 8.23957C17.6957 8.31503 17.7666 8.44498 17.7666 8.58444V16.8663H15.5791V8.58444C15.579 8.44498 15.6499 8.3151 15.7672 8.23957Z" fill="#5680A6"/>
|
||||||
|
<path d="M17.377 8.10996L16.9876 8.36063V16.8662H15.5791V8.62163C15.5791 8.45893 15.6617 8.30738 15.7985 8.21933L16.4136 7.82326C16.5714 7.72167 16.774 7.72167 16.9318 7.82326L17.377 8.10996Z" fill="#497090"/>
|
||||||
|
<path d="M23.5067 16.9318L23.1106 17.547C23.0225 17.6837 22.871 17.7664 22.7083 17.7664H16.6729V15.5791H22.7083C22.871 15.5791 23.0225 15.6617 23.1106 15.7985L23.5067 16.4137C23.6083 16.5715 23.6083 16.774 23.5067 16.9318Z" fill="#5680A6"/>
|
||||||
|
<path d="M19.6131 16.6727C19.6131 17.0594 19.5384 17.4284 19.4018 17.7664H16.6729V15.5791H19.4018C19.5384 15.9171 19.6131 16.2861 19.6131 16.6727Z" fill="#497090"/>
|
||||||
|
<path d="M16.6723 18.2039C17.5182 18.2039 18.2039 17.5182 18.2039 16.6723C18.2039 15.8264 17.5182 15.1406 16.6723 15.1406C15.8264 15.1406 15.1406 15.8264 15.1406 16.6723C15.1406 17.5182 15.8264 18.2039 16.6723 18.2039Z" fill="#F07281"/>
|
||||||
|
<path d="M17.3765 18.0326C17.166 18.1424 16.9265 18.2044 16.6723 18.2044C15.8265 18.2044 15.1406 17.5185 15.1406 16.6728C15.1406 15.827 15.8265 15.1411 16.6723 15.1411C16.9265 15.1411 17.166 15.2031 17.3765 15.313C16.885 15.5672 16.549 16.0813 16.549 16.6728C16.549 17.2644 16.885 17.7783 17.3765 18.0326Z" fill="#EB5569"/>
|
||||||
|
<path d="M34.5329 28.7151V27.6225C34.5329 27.4043 34.3853 27.2137 34.174 27.1592L32.9876 26.853C32.8555 26.3207 32.6457 25.8193 32.37 25.3617L32.9923 24.3064C33.1032 24.1184 33.0728 23.8793 32.9185 23.7249L32.1459 22.9523C31.9916 22.798 31.7524 22.7676 31.5644 22.8785L30.5091 23.5008C30.0516 23.2251 29.5501 23.0153 29.0178 22.8832L28.7116 21.6968C28.6571 21.4855 28.4665 21.3379 28.2483 21.3379H27.1557C26.9375 21.3379 26.7469 21.4855 26.6924 21.6968L26.3862 22.8832C25.8539 23.0153 25.3525 23.2252 24.8949 23.5008L23.8396 22.8785C23.6516 22.7676 23.4125 22.798 23.2581 22.9523L22.4855 23.7249C22.3313 23.8792 22.3009 24.1184 22.4117 24.3064L23.0341 25.3617C22.7583 25.8193 22.5485 26.3207 22.4164 26.853L21.23 27.1592C21.0188 27.2137 20.8711 27.4043 20.8711 27.6225V28.7151C20.8711 28.9333 21.0188 29.1239 21.23 29.1784L22.4164 29.4846C22.5485 30.0169 22.7584 30.5183 23.0341 30.9759L22.4117 32.0312C22.3008 32.2192 22.3313 32.4583 22.4855 32.6127L23.2581 33.3853C23.4124 33.5396 23.6516 33.5699 23.8396 33.4591L24.8949 32.8368C25.3525 33.1125 25.8539 33.3223 26.3862 33.4544L26.6924 34.6408C26.7469 34.8521 26.9375 34.9997 27.1557 34.9997H28.2483C28.4665 34.9997 28.6571 34.8521 28.7116 34.6408L29.0178 33.4544C29.5501 33.3223 30.0516 33.1124 30.5091 32.8368L31.5644 33.4591C31.7524 33.57 31.9915 33.5396 32.1459 33.3853L32.9185 32.6127C33.0728 32.4584 33.1031 32.2192 32.9923 32.0312L32.37 30.9759C32.6457 30.5183 32.8555 30.0169 32.9876 29.4846L34.174 29.1784C34.3853 29.1239 34.5329 28.9333 34.5329 28.7151Z" fill="#88B4F5"/>
|
||||||
|
<path d="M22.2796 27.252V29.0858L23.8246 29.4844C23.9563 30.0168 24.1662 30.5182 24.4423 30.976L23.6317 32.3506L24.4057 33.1246L23.8391 33.4588C23.6511 33.5696 23.4119 33.5393 23.2576 33.3849L22.4853 32.6127C22.331 32.4584 22.3007 32.2192 22.4115 32.0313L23.0338 30.976C22.7577 30.5182 22.5479 30.0168 22.4162 29.4844L21.23 29.1784C21.0187 29.124 20.8711 28.9333 20.8711 28.7151V27.6226C20.8711 27.4044 21.0187 27.2139 21.2299 27.1593L22.4162 26.8527C22.5479 26.321 22.7577 25.8189 23.0338 25.3619L22.4115 24.3064C22.3007 24.1185 22.331 23.8794 22.4853 23.7251L23.2577 22.9523C23.4119 22.7978 23.6512 22.7675 23.8392 22.8784L24.4057 23.2125L23.6317 23.9872L24.4423 25.3619C24.1662 25.8189 23.9564 26.321 23.8246 26.8527L22.2796 27.252Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.7946 33.454L28.1932 34.9997H27.1555C26.9372 34.9997 26.7466 34.8521 26.6921 34.6407L26.3861 33.454C26.1361 33.3927 25.8924 33.3132 25.6572 33.2174L26.303 32.8364C26.7608 33.1124 27.2622 33.3223 27.7946 33.454Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.1555 21.3379H28.1932L27.7946 22.883C27.2622 23.0154 26.7608 23.2252 26.303 23.5006L25.6572 23.1196C25.8925 23.0245 26.1361 22.945 26.3861 22.883L26.6921 21.6969C26.7466 21.4855 26.9373 21.3379 27.1555 21.3379Z" fill="#6EA2F2"/>
|
||||||
|
<path d="M27.7023 31.4612C29.5205 31.4612 30.9944 29.9873 30.9944 28.1691C30.9944 26.3509 29.5205 24.877 27.7023 24.877C25.8841 24.877 24.4102 26.3509 24.4102 28.1691C24.4102 29.9873 25.8841 31.4612 27.7023 31.4612Z" fill="#5680A6"/>
|
||||||
|
<path d="M28.7581 31.2882C28.4264 31.4009 28.0707 31.4615 27.7017 31.4615C25.8841 31.4615 24.4102 29.9875 24.4102 28.1692C24.4102 26.3509 25.8841 24.877 27.7017 24.877C28.0707 24.877 28.4263 24.9375 28.7581 25.0502C27.4588 25.4903 26.5229 26.7206 26.5229 28.1692C26.5229 29.6178 27.4588 30.8481 28.7581 31.2882Z" fill="#497090"/>
|
||||||
|
<path d="M16.6727 6.19265C16.381 6.19265 16.1445 5.95619 16.1445 5.6645V4.30012C16.1445 4.00843 16.381 3.77197 16.6727 3.77197C16.9644 3.77197 17.2008 4.00843 17.2008 4.30012V5.66443C17.2008 5.95612 16.9644 6.19265 16.6727 6.19265Z" fill="#88B4F5"/>
|
||||||
|
<path d="M16.6727 29.5729C16.381 29.5729 16.1445 29.3365 16.1445 29.0448V27.6805C16.1445 27.3888 16.381 27.1523 16.6727 27.1523C16.9644 27.1523 17.2008 27.3888 17.2008 27.6805V29.0448C17.2008 29.3364 16.9644 29.5729 16.6727 29.5729Z" fill="#88B4F5"/>
|
||||||
|
<path d="M5.66492 17.2003H4.30061C4.00892 17.2003 3.77246 16.9639 3.77246 16.6722C3.77246 16.3805 4.00892 16.144 4.30061 16.144H5.66492C5.95661 16.144 6.19307 16.3805 6.19307 16.6722C6.19307 16.9639 5.95661 17.2003 5.66492 17.2003Z" fill="#88B4F5"/>
|
||||||
|
<path d="M29.0448 17.2003H27.6805C27.3887 17.2003 27.1523 16.9639 27.1523 16.6722C27.1523 16.3805 27.3888 16.144 27.6805 16.144H29.0448C29.3366 16.144 29.5729 16.3805 29.5729 16.6722C29.5729 16.9639 29.3365 17.2003 29.0448 17.2003Z" fill="#88B4F5"/>
|
||||||
|
<path d="M5.95843 23.3866C5.77591 23.3866 5.59838 23.2918 5.50056 23.1224C5.35468 22.8698 5.44122 22.5467 5.69388 22.4009L6.8754 21.7188C7.12785 21.573 7.45099 21.6594 7.59687 21.9121C7.74275 22.1647 7.6562 22.4877 7.40355 22.6335L6.22202 23.3157C6.1389 23.3638 6.04805 23.3866 5.95843 23.3866Z" fill="#88B4F5"/>
|
||||||
|
<path d="M26.2065 11.6967C26.0239 11.6967 25.8464 11.6019 25.7486 11.4325C25.6027 11.1799 25.6893 10.8568 25.9419 10.711L27.1234 10.0288C27.376 9.88304 27.699 9.96951 27.8449 10.2222C27.9908 10.4748 27.9042 10.7978 27.6516 10.9436L26.4701 11.6258C26.3869 11.6738 26.296 11.6967 26.2065 11.6967Z" fill="#88B4F5"/>
|
||||||
|
<path d="M7.139 11.6967C7.04938 11.6967 6.9586 11.6739 6.8754 11.6259L5.69388 10.9437C5.44122 10.7978 5.35468 10.4748 5.50056 10.2222C5.64637 9.96957 5.96937 9.88296 6.22202 10.0289L7.40355 10.7111C7.6562 10.8569 7.74275 11.1799 7.59687 11.4325C7.49904 11.602 7.32152 11.6967 7.139 11.6967Z" fill="#88B4F5"/>
|
||||||
|
<path d="M10.4858 27.9149C10.3962 27.9149 10.3054 27.892 10.2222 27.844C9.96954 27.6981 9.883 27.3751 10.0289 27.1225L10.711 25.941C10.8568 25.6883 11.1799 25.6017 11.4325 25.7477C11.6852 25.8935 11.7717 26.2165 11.6258 26.4691L10.9437 27.6506C10.8458 27.8201 10.6683 27.9149 10.4858 27.9149Z" fill="#88B4F5"/>
|
||||||
|
<path d="M22.1752 7.66727C22.0856 7.66727 21.9948 7.64444 21.9116 7.59638C21.659 7.45057 21.5724 7.1275 21.7183 6.87491L22.4005 5.69339C22.5463 5.44073 22.8692 5.35419 23.1219 5.50007C23.3745 5.64588 23.4611 5.96895 23.3153 6.22154L22.6331 7.40306C22.5353 7.57252 22.3578 7.66727 22.1752 7.66727Z" fill="#88B4F5"/>
|
||||||
|
<path d="M11.1689 7.66727C10.9864 7.66727 10.8089 7.57252 10.711 7.40306L10.0289 6.22154C9.883 5.96895 9.96954 5.64588 10.2222 5.50007C10.4747 5.35419 10.7979 5.44073 10.9437 5.69339L11.6258 6.87491C11.7717 7.1275 11.6852 7.45057 11.4325 7.59638C11.3494 7.64437 11.2585 7.66727 11.1689 7.66727Z" fill="#88B4F5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 8.6 KiB |
@ -3,25 +3,27 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
|
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
|
import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
|
||||||
import 'package:syncrow_web/pages/access_management/model/password_model.dart';
|
import 'package:syncrow_web/pages/access_management/model/password_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
|
||||||
import 'package:syncrow_web/services/access_mang_api.dart';
|
import 'package:syncrow_web/services/access_mang_api.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||||
import 'package:syncrow_web/utils/snack_bar.dart';
|
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||||
|
|
||||||
class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||||
AccessBloc() : super((AccessInitial())) {
|
AccessBloc() : super((AccessInitial())) {
|
||||||
on<FetchTableData>(_onFetchTableData);
|
on<FetchTableData>(_onFetchTableData);
|
||||||
// on<TabChangedEvent>(selectFilterTap);
|
|
||||||
on<SelectTime>(selectTime);
|
on<SelectTime>(selectTime);
|
||||||
on<FilterDataEvent>(_filterData);
|
on<FilterDataEvent>(_filterData);
|
||||||
on<ResetSearch>(resetSearch);
|
on<ResetSearch>(resetSearch);
|
||||||
on<TabChangedEvent>(onTabChanged);
|
on<TabChangedEvent>(onTabChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
String startTime = 'Start Date';
|
String startTime = 'Start Date';
|
||||||
String endTime = 'End Date';
|
String endTime = 'End Date';
|
||||||
|
|
||||||
int? effectiveTimeTimeStamp;
|
int? effectiveTimeTimeStamp;
|
||||||
int? expirationTimeTimeStamp;
|
int? expirationTimeTimeStamp;
|
||||||
TextEditingController passwordName = TextEditingController();
|
TextEditingController passwordName = TextEditingController();
|
||||||
|
TextEditingController emailAuthorizer = TextEditingController();
|
||||||
List<PasswordModel> filteredData = [];
|
List<PasswordModel> filteredData = [];
|
||||||
List<PasswordModel> data = [];
|
List<PasswordModel> data = [];
|
||||||
|
|
||||||
@ -62,57 +64,61 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> selectTime(SelectTime event, Emitter<AccessState> emit) async {
|
|
||||||
|
Future<void> selectTime(SelectTime event, Emitter<AccessState> emit,) async {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: event.context,
|
context: event.context,
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
firstDate: DateTime(2015, 8),
|
firstDate: DateTime.now().add(const Duration(days: -5095)),
|
||||||
lastDate: DateTime(2101),
|
lastDate: DateTime.now().add(const Duration(days: 2095)),
|
||||||
);
|
);
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
final selectedDateTime = DateTime(
|
final TimeOfDay? timePicked = await showHourPicker(
|
||||||
picked.year,
|
context: event.context,
|
||||||
picked.month,
|
initialTime: TimeOfDay.now(),
|
||||||
picked.day,
|
|
||||||
);
|
);
|
||||||
final selectedTimestamp = DateTime(
|
|
||||||
selectedDateTime.year,
|
if (timePicked != null) {
|
||||||
selectedDateTime.month,
|
final DateTime selectedDateTime = DateTime(
|
||||||
selectedDateTime.day,
|
picked.year,
|
||||||
selectedDateTime.hour,
|
picked.month,
|
||||||
selectedDateTime.minute,
|
picked.day,
|
||||||
).millisecondsSinceEpoch ~/
|
timePicked.hour,
|
||||||
1000; // Divide by 1000 to remove milliseconds
|
timePicked.minute,
|
||||||
if (event.isStart) {
|
);
|
||||||
if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) {
|
final int selectedTimestamp = selectedDateTime.millisecondsSinceEpoch ~/ 1000;
|
||||||
CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.');
|
if (event.isStart) {
|
||||||
|
if (expirationTimeTimeStamp != null && selectedTimestamp > expirationTimeTimeStamp!) {
|
||||||
|
CustomSnackBar.displaySnackBar('Effective Time cannot be later than Expiration Time.');
|
||||||
|
} else {
|
||||||
|
startTime = selectedDateTime.toString().split('.').first;
|
||||||
|
effectiveTimeTimeStamp = selectedTimestamp;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
startTime =
|
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
|
||||||
selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.');
|
||||||
effectiveTimeTimeStamp = selectedTimestamp;
|
} else {
|
||||||
}
|
endTime = selectedDateTime.toString().split('.').first;
|
||||||
} else {
|
expirationTimeTimeStamp = selectedTimestamp;
|
||||||
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
|
}
|
||||||
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.');
|
|
||||||
} else {
|
|
||||||
endTime = selectedDateTime.toString().split('.').first; // Remove seconds and milliseconds
|
|
||||||
expirationTimeTimeStamp = selectedTimestamp;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit(ChangeTimeState());
|
emit(ChangeTimeState());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> _filterData(FilterDataEvent event, Emitter<AccessState> emit) async {
|
Future<void> _filterData(FilterDataEvent event, Emitter<AccessState> emit) async {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
try {
|
try {
|
||||||
|
print(event.emailAuthorizer?.toLowerCase());
|
||||||
// Convert search text to lower case for case-insensitive search
|
// Convert search text to lower case for case-insensitive search
|
||||||
final searchText = event.passwordName?.toLowerCase() ?? '';
|
final searchText = event.passwordName?.toLowerCase() ?? '';
|
||||||
|
final searchEmailText = event.emailAuthorizer?.toLowerCase() ?? '';
|
||||||
filteredData = data.where((item) {
|
filteredData = data.where((item) {
|
||||||
bool matchesCriteria = true;
|
bool matchesCriteria = true;
|
||||||
|
|
||||||
// Convert timestamp to DateTime and extract date component
|
// Convert timestamp to DateTime and extract date component
|
||||||
DateTime effectiveDate =
|
DateTime effectiveDate =
|
||||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000)
|
DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000)
|
||||||
@ -122,9 +128,8 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000)
|
DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
DateTime effectiveDateOnly =
|
DateTime effectiveDateAndTime = DateTime(effectiveDate.year, effectiveDate.month, effectiveDate.day,effectiveDate.hour,effectiveDate.minute);
|
||||||
DateTime(effectiveDate.year, effectiveDate.month, effectiveDate.day);
|
DateTime invalidDateAndTime = DateTime(invalidDate.year, invalidDate.month, invalidDate.day,invalidDate.hour,invalidDate.minute);
|
||||||
DateTime invalidDateOnly = DateTime(invalidDate.year, invalidDate.month, invalidDate.day);
|
|
||||||
|
|
||||||
// Filter by password name, making the search case-insensitive
|
// Filter by password name, making the search case-insensitive
|
||||||
if (searchText.isNotEmpty) {
|
if (searchText.isNotEmpty) {
|
||||||
@ -133,36 +138,51 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
matchesCriteria = false;
|
matchesCriteria = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (searchEmailText.isNotEmpty) {
|
||||||
// Filter by start date only
|
final bool matchesName = item.authorizerEmail.toString().toLowerCase().contains(searchEmailText);
|
||||||
if (event.startTime != null && event.endTime == null) {
|
if (!matchesName) {
|
||||||
DateTime startDateOnly =
|
matchesCriteria = false;
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
}
|
||||||
startDateOnly = DateTime(startDateOnly.year, startDateOnly.month, startDateOnly.day);
|
}
|
||||||
if (effectiveDateOnly.isBefore(startDateOnly)) {
|
// Filter by start date only
|
||||||
|
if (event.startTime != null && event.endTime == null) {
|
||||||
|
DateTime startDateTime =
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
||||||
|
startDateTime = DateTime(
|
||||||
|
startDateTime.year,
|
||||||
|
startDateTime.month,
|
||||||
|
startDateTime.day,
|
||||||
|
startDateTime.hour,
|
||||||
|
startDateTime.minute);
|
||||||
|
if (effectiveDateAndTime.isBefore(startDateTime)) {
|
||||||
matchesCriteria = false;
|
matchesCriteria = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by end date only
|
// Filter by end date only
|
||||||
if (event.endTime != null && event.startTime == null) {
|
if (event.endTime != null && event.startTime == null) {
|
||||||
DateTime endDateOnly =
|
DateTime startDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||||
endDateOnly = DateTime(endDateOnly.year, endDateOnly.month, endDateOnly.day);
|
startDateTime = DateTime(
|
||||||
if (invalidDateOnly.isAfter(endDateOnly)) {
|
startDateTime.year,
|
||||||
|
startDateTime.month,
|
||||||
|
startDateTime.day,
|
||||||
|
startDateTime.hour,
|
||||||
|
startDateTime.minute
|
||||||
|
);
|
||||||
|
if (invalidDateAndTime.isAfter(startDateTime)) {
|
||||||
matchesCriteria = false;
|
matchesCriteria = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by both start date and end date
|
// Filter by both start date and end date
|
||||||
if (event.startTime != null && event.endTime != null) {
|
if (event.startTime != null && event.endTime != null) {
|
||||||
DateTime startDateOnly =
|
DateTime startDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
||||||
DateTime endDateOnly =
|
DateTime endDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||||
startDateOnly = DateTime(startDateOnly.year, startDateOnly.month, startDateOnly.day);
|
startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day,startDateTime.hour,startDateTime.minute);
|
||||||
endDateOnly = DateTime(endDateOnly.year, endDateOnly.month, endDateOnly.day);
|
endDateTime = DateTime(endDateTime.year, endDateTime.month, endDateTime.day,endDateTime.hour,endDateTime.minute);
|
||||||
if (effectiveDateOnly.isBefore(startDateOnly) || invalidDateOnly.isAfter(endDateOnly)) {
|
if (effectiveDateAndTime.isBefore(startDateTime) || invalidDateAndTime.isAfter(endDateTime)) {
|
||||||
matchesCriteria = false;
|
matchesCriteria = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,12 +206,12 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
|
resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
startTime = 'Start Time';
|
startTime = 'Start Time';
|
||||||
endTime = 'End Time';
|
endTime = 'End Time';
|
||||||
passwordName.clear();
|
passwordName.clear();
|
||||||
|
emailAuthorizer.clear();
|
||||||
selectedIndex = 0;
|
selectedIndex = 0;
|
||||||
effectiveTimeTimeStamp = null;
|
effectiveTimeTimeStamp = null;
|
||||||
expirationTimeTimeStamp = null;
|
expirationTimeTimeStamp = null;
|
||||||
@ -200,9 +220,11 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
|
|
||||||
String timestampToDate(dynamic timestamp) {
|
String timestampToDate(dynamic timestamp) {
|
||||||
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
|
DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
|
||||||
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')}";
|
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')} "
|
||||||
|
" ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> onTabChanged(TabChangedEvent event, Emitter<AccessState> emit) async {
|
Future<void> onTabChanged(TabChangedEvent event, Emitter<AccessState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
@ -227,6 +249,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
add(FilterDataEvent(
|
add(FilterDataEvent(
|
||||||
selectedTabIndex: selectedIndex,
|
selectedTabIndex: selectedIndex,
|
||||||
passwordName: passwordName.text.toLowerCase(),
|
passwordName: passwordName.text.toLowerCase(),
|
||||||
|
emailAuthorizer: emailAuthorizer.text.toLowerCase(),
|
||||||
startTime: effectiveTimeTimeStamp,
|
startTime: effectiveTimeTimeStamp,
|
||||||
endTime: expirationTimeTimeStamp));
|
endTime: expirationTimeTimeStamp));
|
||||||
emit(TableLoaded(filteredData));
|
emit(TableLoaded(filteredData));
|
||||||
@ -234,4 +257,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
emit(FailedState(e.toString()));
|
emit(FailedState(e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,12 +28,14 @@ class SelectTime extends AccessEvent {
|
|||||||
|
|
||||||
class FilterDataEvent extends AccessEvent {
|
class FilterDataEvent extends AccessEvent {
|
||||||
final String? passwordName;
|
final String? passwordName;
|
||||||
|
final String? emailAuthorizer;
|
||||||
final int? startTime;
|
final int? startTime;
|
||||||
final int? endTime;
|
final int? endTime;
|
||||||
final int selectedTabIndex; // Add this field
|
final int selectedTabIndex; // Add this field
|
||||||
|
|
||||||
const FilterDataEvent({
|
const FilterDataEvent({
|
||||||
this.passwordName,
|
this.passwordName,
|
||||||
|
this.emailAuthorizer,
|
||||||
this.startTime,
|
this.startTime,
|
||||||
this.endTime,
|
this.endTime,
|
||||||
required this.selectedTabIndex, // Initialize this field
|
required this.selectedTabIndex, // Initialize this field
|
||||||
|
@ -6,10 +6,13 @@ class PasswordModel {
|
|||||||
final dynamic effectiveTime;
|
final dynamic effectiveTime;
|
||||||
final dynamic passwordCreated;
|
final dynamic passwordCreated;
|
||||||
final dynamic createdTime;
|
final dynamic createdTime;
|
||||||
final dynamic passwordName; // New field
|
final dynamic passwordName;
|
||||||
final AccessStatus passwordStatus;
|
final AccessStatus passwordStatus;
|
||||||
final AccessType passwordType;
|
final AccessType passwordType;
|
||||||
final dynamic deviceUuid;
|
final dynamic deviceUuid;
|
||||||
|
final dynamic authorizerEmail;
|
||||||
|
final dynamic authorizerDate;
|
||||||
|
final dynamic deviceName;
|
||||||
|
|
||||||
PasswordModel({
|
PasswordModel({
|
||||||
this.passwordId,
|
this.passwordId,
|
||||||
@ -17,10 +20,13 @@ class PasswordModel {
|
|||||||
this.effectiveTime,
|
this.effectiveTime,
|
||||||
this.passwordCreated,
|
this.passwordCreated,
|
||||||
this.createdTime,
|
this.createdTime,
|
||||||
this.passwordName, // New field
|
this.passwordName,
|
||||||
required this.passwordStatus,
|
required this.passwordStatus,
|
||||||
required this.passwordType,
|
required this.passwordType,
|
||||||
this.deviceUuid,
|
this.deviceUuid,
|
||||||
|
this.authorizerEmail,
|
||||||
|
this.authorizerDate,
|
||||||
|
this.deviceName,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory PasswordModel.fromJson(Map<String, dynamic> json) {
|
factory PasswordModel.fromJson(Map<String, dynamic> json) {
|
||||||
@ -34,6 +40,9 @@ class PasswordModel {
|
|||||||
passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
|
passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
|
||||||
passwordType: AccessTypeExtension.fromString(json['passwordType']),
|
passwordType: AccessTypeExtension.fromString(json['passwordType']),
|
||||||
deviceUuid: json['deviceUuid'],
|
deviceUuid: json['deviceUuid'],
|
||||||
|
authorizerEmail: json['authorizerEmail'],
|
||||||
|
authorizerDate: json['authorizerDate'],
|
||||||
|
deviceName: json['deviceName'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,10 +53,13 @@ class PasswordModel {
|
|||||||
'effectiveTime': effectiveTime,
|
'effectiveTime': effectiveTime,
|
||||||
'passwordCreated': passwordCreated,
|
'passwordCreated': passwordCreated,
|
||||||
'createdTime': createdTime,
|
'createdTime': createdTime,
|
||||||
'passwodName': passwordName, // New field
|
'passwordName': passwordName, // New field
|
||||||
'passwordStatus': passwordStatus,
|
'passwordStatus': passwordStatus,
|
||||||
'passwordType': passwordType,
|
'passwordType': passwordType,
|
||||||
'deviceUuid': deviceUuid,
|
'deviceUuid': deviceUuid,
|
||||||
|
'authorizerEmail': authorizerEmail,
|
||||||
|
'authorizerDate': authorizerDate,
|
||||||
|
'deviceName': deviceName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,13 +24,14 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
final isLargeScreen = isLargeScreenSize(context);
|
final isLargeScreen = isLargeScreenSize(context);
|
||||||
final isSmallScreen = isSmallScreenSize(context);
|
final isSmallScreen = isSmallScreenSize(context);
|
||||||
final isHalfMediumScreen = isHafMediumScreenSize(context);
|
final isHalfMediumScreen = isHafMediumScreenSize(context);
|
||||||
final padding = isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15);
|
final padding = isLargeScreen ? const EdgeInsets.all(30) : const EdgeInsets.all(15);
|
||||||
|
|
||||||
return WebScaffold(
|
return WebScaffold(
|
||||||
enableMenuSideba: false,
|
enableMenuSidebar: false,
|
||||||
appBarTitle: FittedBox(
|
appBarTitle: FittedBox(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Access Management',
|
'Access Management',
|
||||||
@ -85,7 +86,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
_buildVisitorAdminPasswords(context, accessBloc),
|
_buildVisitorAdminPasswords(context, accessBloc),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DynamicTable(
|
child: DynamicTable(
|
||||||
tableName: 'AccessManagement',
|
tableName: 'AccessManagement',
|
||||||
uuidIndex: 1,
|
uuidIndex: 1,
|
||||||
withSelectAll: true,
|
withSelectAll: true,
|
||||||
@ -96,7 +97,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
headers: const [
|
headers: const [
|
||||||
'Name',
|
'Name',
|
||||||
'Access Type',
|
'Access Type',
|
||||||
'Access Period',
|
'Access Start',
|
||||||
|
'Access End',
|
||||||
'Accessible Device',
|
'Accessible Device',
|
||||||
'Authorizer',
|
'Authorizer',
|
||||||
'Authorization Date & Time',
|
'Authorization Date & Time',
|
||||||
@ -106,10 +108,11 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
return [
|
return [
|
||||||
item.passwordName,
|
item.passwordName,
|
||||||
item.passwordType.value,
|
item.passwordType.value,
|
||||||
('${accessBloc.timestampToDate(item.effectiveTime)} - ${accessBloc.timestampToDate(item.invalidTime)}'),
|
accessBloc.timestampToDate(item.effectiveTime),
|
||||||
item.deviceUuid.toString(),
|
accessBloc.timestampToDate(item.invalidTime),
|
||||||
'',
|
item.deviceName.toString(),
|
||||||
'',
|
item.authorizerEmail.toString(),
|
||||||
|
accessBloc.timestampToDate(item.invalidTime),
|
||||||
item.passwordStatus.value,
|
item.passwordStatus.value,
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@ -166,6 +169,8 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) {
|
Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) {
|
||||||
|
TimeOfDay _selectedTime = TimeOfDay.now();
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
@ -182,6 +187,17 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 15),
|
const SizedBox(width: 15),
|
||||||
|
SizedBox(
|
||||||
|
width: 250,
|
||||||
|
child: CustomWebTextField(
|
||||||
|
controller: accessBloc.emailAuthorizer,
|
||||||
|
height: 43,
|
||||||
|
isRequired: false,
|
||||||
|
textFieldName: 'Authorizer',
|
||||||
|
description: '',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: DateTimeWebWidget(
|
child: DateTimeWebWidget(
|
||||||
icon: Assets.calendarIcon,
|
icon: Assets.calendarIcon,
|
||||||
@ -189,7 +205,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
title: 'Access Time',
|
title: 'Access Time',
|
||||||
size: MediaQuery.of(context).size,
|
size: MediaQuery.of(context).size,
|
||||||
endTime: () {
|
endTime: () {
|
||||||
accessBloc.add(SelectTime(context: context, isStart: false));
|
accessBloc.add(SelectTime(context: context, isStart: false));
|
||||||
},
|
},
|
||||||
startTime: () {
|
startTime: () {
|
||||||
accessBloc.add(SelectTime(context: context, isStart: true));
|
accessBloc.add(SelectTime(context: context, isStart: true));
|
||||||
@ -202,6 +218,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
SearchResetButtons(
|
SearchResetButtons(
|
||||||
onSearch: () {
|
onSearch: () {
|
||||||
accessBloc.add(FilterDataEvent(
|
accessBloc.add(FilterDataEvent(
|
||||||
|
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
|
||||||
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
||||||
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
||||||
startTime: accessBloc.effectiveTimeTimeStamp,
|
startTime: accessBloc.effectiveTimeTimeStamp,
|
||||||
@ -247,10 +264,12 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
SearchResetButtons(
|
SearchResetButtons(
|
||||||
onSearch: () {
|
onSearch: () {
|
||||||
accessBloc.add(FilterDataEvent(
|
accessBloc.add(FilterDataEvent(
|
||||||
|
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
|
||||||
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
||||||
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
||||||
startTime: accessBloc.effectiveTimeTimeStamp,
|
startTime: accessBloc.effectiveTimeTimeStamp,
|
||||||
endTime: accessBloc.expirationTimeTimeStamp));
|
endTime: accessBloc.expirationTimeTimeStamp
|
||||||
|
));
|
||||||
},
|
},
|
||||||
onReset: () {
|
onReset: () {
|
||||||
accessBloc.add(ResetSearch());
|
accessBloc.add(ResetSearch());
|
||||||
@ -259,4 +278,6 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,35 +31,60 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
////////////////////////////// forget password //////////////////////////////////
|
////////////////////////////// forget password //////////////////////////////////
|
||||||
final TextEditingController forgetEmailController = TextEditingController();
|
final TextEditingController forgetEmailController = TextEditingController();
|
||||||
final TextEditingController forgetPasswordController =
|
final TextEditingController forgetPasswordController = TextEditingController();
|
||||||
TextEditingController();
|
|
||||||
final TextEditingController forgetOtp = TextEditingController();
|
final TextEditingController forgetOtp = TextEditingController();
|
||||||
final forgetFormKey = GlobalKey<FormState>();
|
final forgetFormKey = GlobalKey<FormState>();
|
||||||
|
final forgetEmailKey = GlobalKey<FormState>();
|
||||||
|
final forgetRegionKey = GlobalKey<FormState>();
|
||||||
late bool checkValidate = false;
|
late bool checkValidate = false;
|
||||||
|
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
int _remainingTime = 0;
|
int _remainingTime = 0;
|
||||||
List<RegionModel>? regionList = [RegionModel(name: 'name', id: 'id')];
|
List<RegionModel>? regionList = [RegionModel(name: 'name', id: 'id')];
|
||||||
|
|
||||||
Future<void> _onStartTimer(
|
Future _onStartTimer(StartTimerEvent event, Emitter<AuthState> emit) async {
|
||||||
StartTimerEvent event, Emitter<AuthState> emit) async {
|
|
||||||
if (_validateInputs(emit)) return;
|
if (_validateInputs(emit)) return;
|
||||||
if (_timer != null && _timer!.isActive) {
|
if (_timer != null && _timer!.isActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_remainingTime = 1;
|
_remainingTime = 1;
|
||||||
add(UpdateTimerEvent(
|
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
try {
|
||||||
_remainingTime = (await AuthenticationAPI.sendOtp(
|
forgetEmailValidate = '';
|
||||||
email: forgetEmailController.text, regionUuid: regionUuid))!;
|
_remainingTime = (await AuthenticationAPI.sendOtp(
|
||||||
|
email: forgetEmailController.text, regionUuid: regionUuid))!;
|
||||||
|
} on DioException catch (e) {
|
||||||
|
if (e.response!.statusCode == 400) {
|
||||||
|
final errorData = e.response!.data;
|
||||||
|
String errorMessage = errorData['message'];
|
||||||
|
if (errorMessage == 'User not found') {
|
||||||
|
validate = 'Invalid Credential';
|
||||||
|
emit(AuthInitialState());
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
validate = '';
|
||||||
|
_remainingTime = errorData['data']['cooldown'] ?? 1;
|
||||||
|
emit(AuthInitialState());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit(AuthInitialState());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
emit(AuthInitialState());
|
||||||
|
} catch (e) {
|
||||||
|
emit(AuthInitialState());
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||||
_remainingTime--;
|
_remainingTime--;
|
||||||
if (_remainingTime <= 0) {
|
if (_remainingTime <= 0) {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||||
} else {
|
} else {
|
||||||
add(UpdateTimerEvent(
|
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -69,36 +94,37 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changePassword(
|
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||||
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
emit(LoadingForgetState());
|
||||||
try {
|
try {
|
||||||
emit(LoadingForgetState());
|
var response = await AuthenticationAPI.verifyOtp(
|
||||||
var response = await AuthenticationAPI.verifyOtp(
|
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
||||||
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
if (response == true) {
|
||||||
if (response == true) {
|
await AuthenticationAPI.forgetPassword(
|
||||||
await AuthenticationAPI.forgetPassword(
|
password: forgetPasswordController.text,
|
||||||
password: forgetPasswordController.text,
|
email: forgetEmailController.text);
|
||||||
email: forgetEmailController.text);
|
_timer?.cancel();
|
||||||
_timer?.cancel();
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(SuccessForgetState());
|
||||||
emit(SuccessForgetState());
|
}
|
||||||
|
} on DioException catch (e) {
|
||||||
|
final errorData = e.response!.data;
|
||||||
|
String errorMessage = errorData['message'];
|
||||||
|
if (errorMessage == 'this email is not registered') {
|
||||||
|
validate = 'Invalid Credentials!';
|
||||||
|
emit(AuthInitialState());
|
||||||
|
} else if (errorMessage == "You entered wrong otp") {
|
||||||
|
forgetValidate = 'Wrong one time password.';
|
||||||
|
emit(AuthInitialState());
|
||||||
|
} else if (errorMessage == "OTP expired") {
|
||||||
|
forgetValidate = 'One time password has been expired.';
|
||||||
|
emit(AuthInitialState());
|
||||||
|
} else {
|
||||||
|
validate = '';
|
||||||
|
emit(AuthInitialState());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
on DioException catch (e) {
|
|
||||||
final errorData = e.response!.data;
|
|
||||||
String errorMessage = errorData['message'];
|
|
||||||
if(errorMessage=='this email is not registered'){
|
|
||||||
validate='Invalid Credentials!';
|
|
||||||
emit(AuthInitialState());
|
|
||||||
}else if (errorMessage == "You entered wrong otp") {
|
|
||||||
forgetValidate = 'Wrong one time password.';
|
|
||||||
emit(AuthInitialState());
|
|
||||||
} else if (errorMessage == "OTP expired") {
|
|
||||||
forgetValidate = 'One time password has been expired.';
|
|
||||||
emit(AuthInitialState());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String? validateCode(String? value) {
|
String? validateCode(String? value) {
|
||||||
@ -109,9 +135,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
||||||
emit(TimerState(
|
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
|
||||||
isButtonEnabled: event.isButtonEnabled,
|
|
||||||
remainingTime: event.remainingTime));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////// login /////////////////////////////////////
|
///////////////////////////////////// login /////////////////////////////////////
|
||||||
@ -125,6 +149,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
String otpCode = '';
|
String otpCode = '';
|
||||||
String validate = '';
|
String validate = '';
|
||||||
String forgetValidate = '';
|
String forgetValidate = '';
|
||||||
|
String forgetEmailValidate = '';
|
||||||
String regionUuid = '';
|
String regionUuid = '';
|
||||||
static Token token = Token.emptyConstructor();
|
static Token token = Token.emptyConstructor();
|
||||||
static UserModel? user;
|
static UserModel? user;
|
||||||
@ -142,9 +167,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
token = await AuthenticationAPI.loginWithEmail(
|
token = await AuthenticationAPI.loginWithEmail(
|
||||||
model: LoginWithEmailModel(
|
model: LoginWithEmailModel(
|
||||||
email: event.username,
|
email: event.username, password: event.password, regionUuid: event.regionUuid),
|
||||||
password: event.password,
|
|
||||||
regionUuid: event.regionUuid),
|
|
||||||
);
|
);
|
||||||
} catch (failure) {
|
} catch (failure) {
|
||||||
validate = 'Invalid Credentials!';
|
validate = 'Invalid Credentials!';
|
||||||
@ -154,8 +177,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
if (token.accessTokenIsNotEmpty) {
|
if (token.accessTokenIsNotEmpty) {
|
||||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
await storage.write(
|
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||||
key: Token.loginAccessTokenKey, value: token.accessToken);
|
|
||||||
const FlutterSecureStorage().write(
|
const FlutterSecureStorage().write(
|
||||||
key: UserModel.userUuidKey,
|
key: UserModel.userUuidKey,
|
||||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||||
@ -213,9 +235,10 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
return 'Email is required';
|
return 'Email is required';
|
||||||
} else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
} else if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {
|
||||||
return 'Enter a valid email address';
|
return 'Enter a valid email address';
|
||||||
} else if (regionUuid == '') {
|
|
||||||
return 'Please select your region';
|
|
||||||
}
|
}
|
||||||
|
// else if (regionUuid == '') {
|
||||||
|
// return 'Please select your region';
|
||||||
|
// }
|
||||||
validate = '';
|
validate = '';
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -231,7 +254,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(LoadingForgetState());
|
emit(LoadingForgetState());
|
||||||
final nameError = validateEmail(forgetEmailController.text);
|
final nameError = validateEmail(forgetEmailController.text);
|
||||||
if (nameError != null) {
|
if (nameError != null) {
|
||||||
emit(FailureForgetState(error: nameError));
|
forgetEmailValidate = nameError;
|
||||||
|
emit(AuthInitialState());
|
||||||
|
// emit(FailureForgetState(error: nameError));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -310,14 +335,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
static Future<String> getTokenAndValidate() async {
|
static Future<String> getTokenAndValidate() async {
|
||||||
try {
|
try {
|
||||||
const storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
|
final firstLaunch =
|
||||||
StringsManager.firstLaunch) ??
|
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
|
||||||
true;
|
|
||||||
if (firstLaunch) {
|
if (firstLaunch) {
|
||||||
storage.deleteAll();
|
storage.deleteAll();
|
||||||
}
|
}
|
||||||
await SharedPreferencesHelper.saveBoolToSP(
|
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
|
||||||
StringsManager.firstLaunch, false);
|
|
||||||
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
return 'Token not found';
|
return 'Token not found';
|
||||||
@ -353,6 +376,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
try {
|
try {
|
||||||
emit(AuthLoading());
|
emit(AuthLoading());
|
||||||
regionUuid = event.val;
|
regionUuid = event.val;
|
||||||
|
add(CheckEnableEvent());
|
||||||
emit(AuthInitialState());
|
emit(AuthInitialState());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(FailureForgetState(error: e.toString()));
|
emit(FailureForgetState(error: e.toString()));
|
||||||
@ -369,9 +393,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
final String formattedTime = [
|
final String formattedTime = [
|
||||||
if (days > 0) '${days}d', // Append 'd' for days
|
if (days > 0) '${days}d', // Append 'd' for days
|
||||||
if (days > 0 || hours > 0)
|
if (days > 0 || hours > 0)
|
||||||
hours
|
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
|
||||||
.toString()
|
|
||||||
.padLeft(2, '0'), // Show hours if there are days or hours
|
|
||||||
minutes.toString().padLeft(2, '0'),
|
minutes.toString().padLeft(2, '0'),
|
||||||
seconds.toString().padLeft(2, '0'),
|
seconds.toString().padLeft(2, '0'),
|
||||||
].join(':');
|
].join(':');
|
||||||
@ -409,4 +431,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
forgetValidate = '';
|
forgetValidate = '';
|
||||||
emit(LoginInitial());
|
emit(LoginInitial());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static logout() {
|
||||||
|
const storage = FlutterSecureStorage();
|
||||||
|
storage.deleteAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,43 +139,51 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
Form(
|
||||||
child: _buildDropdownField(context, forgetBloc, size)
|
key: forgetBloc.forgetRegionKey,
|
||||||
)
|
child: SizedBox(
|
||||||
|
child: _buildDropdownField(
|
||||||
|
context, forgetBloc, size)))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Column(
|
Form(
|
||||||
crossAxisAlignment:
|
key: forgetBloc.forgetEmailKey,
|
||||||
CrossAxisAlignment.start,
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
children: [
|
CrossAxisAlignment.start,
|
||||||
Text(
|
mainAxisAlignment:
|
||||||
"Account",
|
MainAxisAlignment.start,
|
||||||
style: Theme.of(context)
|
children: [
|
||||||
.textTheme
|
Text(
|
||||||
.bodySmall!
|
"Account",
|
||||||
.copyWith(
|
style: Theme.of(context)
|
||||||
fontSize: 14,
|
.textTheme
|
||||||
fontWeight: FontWeight.w400),
|
.bodySmall!
|
||||||
),
|
.copyWith(
|
||||||
const SizedBox(height: 10),
|
fontSize: 14,
|
||||||
SizedBox(
|
fontWeight:
|
||||||
child: TextFormField(
|
FontWeight.w400),
|
||||||
controller:forgetBloc.forgetEmailController ,
|
),
|
||||||
validator: forgetBloc.validateEmail,
|
const SizedBox(height: 10),
|
||||||
decoration: textBoxDecoration()!
|
SizedBox(
|
||||||
.copyWith(
|
child: TextFormField(
|
||||||
hintText: 'Enter your email',
|
controller: forgetBloc.forgetEmailController,
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
validator: forgetBloc.validateEmail,
|
||||||
color: ColorsManager.grayColor,
|
decoration:
|
||||||
fontWeight: FontWeight.w400)),
|
textBoxDecoration()!.copyWith(
|
||||||
style: const TextStyle(
|
hintText: 'Enter your email',
|
||||||
color: Colors.black),
|
hintStyle: Theme.of(context)
|
||||||
),
|
.textTheme.bodySmall!.copyWith(
|
||||||
),
|
color: ColorsManager.grayColor,
|
||||||
],
|
fontWeight: FontWeight.w400),
|
||||||
),
|
),
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
const SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment:
|
||||||
@ -201,27 +209,32 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
decoration:
|
decoration:
|
||||||
textBoxDecoration()!.copyWith(
|
textBoxDecoration()!.copyWith(
|
||||||
hintText: 'Enter Code',
|
hintText: 'Enter Code',
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
hintStyle: Theme.of(context).textTheme
|
||||||
color: ColorsManager.grayColor,
|
.bodySmall!.copyWith(
|
||||||
fontWeight: FontWeight.w400),
|
color: ColorsManager.grayColor,
|
||||||
|
fontWeight: FontWeight.w400),
|
||||||
suffixIcon: SizedBox(
|
suffixIcon: SizedBox(
|
||||||
width: 100,
|
width: 100,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: state is TimerState &&
|
onTap: state is TimerState &&
|
||||||
!state.isButtonEnabled &&
|
!state.isButtonEnabled &&
|
||||||
state.remainingTime !=
|
state.remainingTime != 1
|
||||||
1
|
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
forgetBloc.add(
|
if (forgetBloc.forgetEmailKey.currentState!.validate()||forgetBloc.forgetRegionKey.currentState!.validate()) {
|
||||||
StartTimerEvent());
|
if(forgetBloc.forgetRegionKey.currentState!.validate()){
|
||||||
|
forgetBloc.add(StartTimerEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: state is TimerState &&
|
color: state
|
||||||
!state.isButtonEnabled
|
is TimerState &&
|
||||||
|
!state
|
||||||
|
.isButtonEnabled
|
||||||
? Colors.grey
|
? Colors.grey
|
||||||
: ColorsManager
|
: ColorsManager
|
||||||
.btnColor,
|
.btnColor,
|
||||||
@ -235,7 +248,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
color: Colors.black),
|
color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (forgetBloc.forgetValidate != '') // Check if there is a validation message
|
if (forgetBloc.forgetValidate !=
|
||||||
|
'') // Check if there is a validation message
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(top: 8.0),
|
const EdgeInsets.only(top: 8.0),
|
||||||
@ -267,16 +281,45 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
validator: forgetBloc.passwordValidator,
|
obscureText: forgetBloc.obscureText,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType:
|
||||||
controller: forgetBloc.forgetPasswordController,
|
TextInputType.visiblePassword,
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
validator:
|
||||||
|
forgetBloc.passwordValidator,
|
||||||
|
controller: forgetBloc
|
||||||
|
.forgetPasswordController,
|
||||||
|
decoration:
|
||||||
|
textBoxDecoration()!.copyWith(
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
forgetBloc.add(
|
||||||
|
PasswordVisibleEvent(
|
||||||
|
newValue: forgetBloc
|
||||||
|
.obscureText));
|
||||||
|
},
|
||||||
|
icon: SizedBox(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
forgetBloc.obscureText
|
||||||
|
? Assets.visiblePassword
|
||||||
|
: Assets
|
||||||
|
.invisiblePassword,
|
||||||
|
height: 15,
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
hintText: 'At least 8 characters',
|
hintText: 'At least 8 characters',
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
hintStyle: Theme.of(context)
|
||||||
color: ColorsManager.grayColor,
|
.textTheme
|
||||||
fontWeight: FontWeight.w400),
|
.bodySmall!
|
||||||
|
.copyWith(
|
||||||
|
color:
|
||||||
|
ColorsManager.grayColor,
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.w400),
|
||||||
),
|
),
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(
|
||||||
|
color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -297,10 +340,12 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
ColorsManager.btnColor,
|
ColorsManager.btnColor,
|
||||||
child: const Text('Submit'),
|
child: const Text('Submit'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (forgetBloc
|
if (forgetBloc.forgetFormKey.currentState!.validate() ||
|
||||||
.forgetFormKey.currentState!
|
forgetBloc.forgetEmailKey.currentState!.validate() ) {
|
||||||
.validate()) {
|
if( forgetBloc.forgetEmailKey.currentState!.validate()
|
||||||
forgetBloc.add(ChangePasswordEvent());
|
&&forgetBloc.forgetFormKey.currentState!.validate() ){
|
||||||
|
forgetBloc.add(ChangePasswordEvent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -318,33 +363,29 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: size.width * 0.2,
|
child: Center(
|
||||||
child: Row(
|
child: Wrap(
|
||||||
mainAxisAlignment:
|
children: [
|
||||||
MainAxisAlignment.center,
|
const Text(
|
||||||
crossAxisAlignment:
|
"Do you have an account? ",
|
||||||
CrossAxisAlignment.center,
|
style:
|
||||||
children: [
|
TextStyle(color: Colors.white),
|
||||||
const Flexible(
|
),
|
||||||
child: Text(
|
InkWell(
|
||||||
"Do you have an account? ",
|
onTap: () {
|
||||||
style: TextStyle(color: Colors.white),
|
forgetBloc.add(StopTimerEvent());
|
||||||
)),
|
Navigator.pop(context);
|
||||||
InkWell(
|
},
|
||||||
onTap: () {
|
child: const Text(
|
||||||
forgetBloc.add(StopTimerEvent());
|
"Sign in",
|
||||||
Navigator.pop(context);
|
),
|
||||||
},
|
),
|
||||||
child: const Flexible(
|
],
|
||||||
child: Text(
|
),
|
||||||
"Sign in",
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 15.0),
|
const SizedBox(height: 15.0),
|
||||||
@ -365,6 +406,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDropdownField(
|
Widget _buildDropdownField(
|
||||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
final TextEditingController textEditingController = TextEditingController();
|
final TextEditingController textEditingController = TextEditingController();
|
||||||
@ -375,99 +417,153 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
"Country/Region",
|
"Country/Region",
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Container(
|
SizedBox(
|
||||||
height: 50,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.boxColor,
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(8)),
|
|
||||||
),
|
|
||||||
width: size.width * 0.9,
|
width: size.width * 0.9,
|
||||||
child: DropdownButtonHideUnderline(
|
child: FormField<String>(
|
||||||
child: DropdownButton2<String>(
|
validator: (value) {
|
||||||
style: TextStyle(color: Colors.black),
|
if (value == null || value.isEmpty) {
|
||||||
isExpanded: true,
|
return 'Please select a country/region';
|
||||||
hint: Text(
|
}
|
||||||
'Select your region/country',
|
return null;
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
},
|
||||||
color: ColorsManager.grayColor,
|
builder: (FormFieldState<String> field) {
|
||||||
fontWeight: FontWeight.w400,
|
return InputDecorator(
|
||||||
),
|
decoration: InputDecoration(
|
||||||
overflow: TextOverflow.ellipsis,
|
contentPadding:
|
||||||
),
|
const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
|
||||||
items: loginBloc.regionList!.map((RegionModel region) {
|
errorText: field.errorText,
|
||||||
return DropdownMenuItem<String>(
|
filled:
|
||||||
value: region.id, // Use region.id as the value
|
true, // Ensure the dropdown is filled with the background color
|
||||||
child: Text(
|
fillColor: ColorsManager
|
||||||
region.name,
|
.boxColor, // Match the dropdown container color
|
||||||
overflow: TextOverflow.ellipsis,
|
border: OutlineInputBorder(
|
||||||
maxLines: 1,
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: field.hasError ? Colors.red : Colors.transparent,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
enabledBorder: OutlineInputBorder(
|
||||||
}).toList(),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
value: loginBloc.regionList!.any(
|
borderSide: BorderSide(
|
||||||
(region) => region.id == loginBloc.regionUuid,)
|
color:
|
||||||
? loginBloc.regionUuid
|
field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||||
: null,
|
width: 1.5,
|
||||||
onChanged: (String? value) {
|
),
|
||||||
if (value != null) {
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
loginBloc.add(SelectRegionEvent(
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
val: value,
|
borderSide: BorderSide(
|
||||||
));
|
color:
|
||||||
}
|
field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||||
},
|
width: 1.5,
|
||||||
buttonStyleData: const ButtonStyleData(
|
),
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
),
|
||||||
height: 40,
|
errorBorder: OutlineInputBorder(
|
||||||
width: double.infinity,
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
),
|
borderSide: BorderSide(
|
||||||
dropdownStyleData: DropdownStyleData(
|
color: Colors.red,
|
||||||
maxHeight: size.height * 0.70,
|
width: 1.5,
|
||||||
),
|
|
||||||
menuItemStyleData: const MenuItemStyleData(
|
|
||||||
height: 40,
|
|
||||||
),
|
|
||||||
dropdownSearchData: DropdownSearchData(
|
|
||||||
searchController: textEditingController,
|
|
||||||
searchInnerWidgetHeight: 50,
|
|
||||||
searchInnerWidget: Container(
|
|
||||||
height: 50,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
child: TextFormField(
|
|
||||||
style: const TextStyle(color: Colors.black),
|
|
||||||
controller: textEditingController,
|
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
|
||||||
errorStyle: const TextStyle(height: 0),
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12,
|
|
||||||
horizontal: 10,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
searchMatchFn: (item, searchValue) {
|
child: DropdownButtonHideUnderline(
|
||||||
// Use the item's child text (region name) for searching.
|
child: DropdownButton2<String>(
|
||||||
final regionName = (item.child as Text).data?.toLowerCase() ?? '';
|
isExpanded: true,
|
||||||
final search = searchValue.toLowerCase().trim();
|
hint: Text(
|
||||||
// Debugging print statement to ensure values are captured correctly.
|
'Select your region/country',
|
||||||
// Return true if the region name contains the search term.
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
return regionName.contains(search);
|
color: ColorsManager.grayColor,
|
||||||
},
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
onMenuStateChange: (isOpen) {
|
overflow: TextOverflow.ellipsis,
|
||||||
if (!isOpen) {
|
),
|
||||||
textEditingController.clear();
|
items: loginBloc.regionList!.map((RegionModel region) {
|
||||||
}
|
return DropdownMenuItem<String>(
|
||||||
},
|
value: region.id,
|
||||||
),
|
child: Text(
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
|
region.name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
value: loginBloc.regionList!
|
||||||
|
.any((region) => region.id == loginBloc.regionUuid)
|
||||||
|
? loginBloc.regionUuid
|
||||||
|
: null,
|
||||||
|
onChanged: (String? value) {
|
||||||
|
if (value != null) {
|
||||||
|
loginBloc.add(SelectRegionEvent(val: value));
|
||||||
|
field.didChange(
|
||||||
|
value); // Notify the form field of the change
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buttonStyleData: const ButtonStyleData(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
height: 40,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
dropdownStyleData: DropdownStyleData(
|
||||||
|
maxHeight: size.height * 0.70,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ColorsManager
|
||||||
|
.boxColor, // Match dropdown background color to the container
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
menuItemStyleData: const MenuItemStyleData(
|
||||||
|
height: 40,
|
||||||
|
),
|
||||||
|
dropdownSearchData: DropdownSearchData(
|
||||||
|
searchController: textEditingController,
|
||||||
|
searchInnerWidgetHeight: 50,
|
||||||
|
searchInnerWidget: Container(
|
||||||
|
height: 50,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 4),
|
||||||
|
child: TextFormField(
|
||||||
|
style: const TextStyle(color: Colors.black),
|
||||||
|
controller: textEditingController,
|
||||||
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
|
errorStyle: const TextStyle(height: 0),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 12,
|
||||||
|
horizontal: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
searchMatchFn: (item, searchValue) {
|
||||||
|
final regionName =
|
||||||
|
(item.child as Text).data?.toLowerCase() ?? '';
|
||||||
|
final search = searchValue.toLowerCase().trim();
|
||||||
|
return regionName.contains(search);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onMenuStateChange: (isOpen) {
|
||||||
|
if (!isOpen) {
|
||||||
|
textEditingController.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ class DefaultButton extends StatelessWidget {
|
|||||||
this.borderRadius,
|
this.borderRadius,
|
||||||
this.height,
|
this.height,
|
||||||
this.padding,
|
this.padding,
|
||||||
|
this.borderColor,
|
||||||
|
this.elevation,
|
||||||
});
|
});
|
||||||
final void Function()? onPressed;
|
final void Function()? onPressed;
|
||||||
final Widget child;
|
final Widget child;
|
||||||
@ -31,6 +33,9 @@ class DefaultButton extends StatelessWidget {
|
|||||||
final ButtonStyle? customButtonStyle;
|
final ButtonStyle? customButtonStyle;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final Color? foregroundColor;
|
final Color? foregroundColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
final double? elevation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
@ -39,40 +44,42 @@ class DefaultButton extends StatelessWidget {
|
|||||||
? null
|
? null
|
||||||
: customButtonStyle ??
|
: customButtonStyle ??
|
||||||
ButtonStyle(
|
ButtonStyle(
|
||||||
textStyle: MaterialStateProperty.all(
|
textStyle: WidgetStateProperty.all(
|
||||||
customTextStyle ??
|
customTextStyle ??
|
||||||
Theme.of(context).textTheme.bodySmall!.copyWith(
|
Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
color: foregroundColor,
|
color: foregroundColor,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
foregroundColor: MaterialStateProperty.all(
|
foregroundColor: WidgetStateProperty.all(
|
||||||
isSecondary
|
isSecondary
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: enabled
|
: enabled
|
||||||
? foregroundColor ?? Colors.white
|
? foregroundColor ?? Colors.white
|
||||||
: Colors.black,
|
: Colors.black,
|
||||||
),
|
),
|
||||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
backgroundColor: WidgetStateProperty.resolveWith<Color>(
|
||||||
(Set<MaterialState> states) {
|
(Set<WidgetState> states) {
|
||||||
return enabled
|
return enabled
|
||||||
? backgroundColor ?? ColorsManager.primaryColor
|
? backgroundColor ?? ColorsManager.primaryColor
|
||||||
: Colors.black.withOpacity(0.2);
|
: Colors.black.withOpacity(0.2);
|
||||||
}),
|
}),
|
||||||
shape: MaterialStateProperty.all(
|
shape: WidgetStateProperty.all(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
|
side: BorderSide(color: borderColor ?? Colors.transparent),
|
||||||
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
borderRadius: BorderRadius.circular(borderRadius ?? 20),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
fixedSize: MaterialStateProperty.all(
|
fixedSize: WidgetStateProperty.all(
|
||||||
const Size.fromHeight(50),
|
const Size.fromHeight(50),
|
||||||
),
|
),
|
||||||
padding: MaterialStateProperty.all(
|
padding: WidgetStateProperty.all(
|
||||||
EdgeInsets.all(padding ?? 10),
|
EdgeInsets.all(padding ?? 10),
|
||||||
),
|
),
|
||||||
minimumSize: MaterialStateProperty.all(
|
minimumSize: WidgetStateProperty.all(
|
||||||
const Size.fromHeight(50),
|
const Size.fromHeight(50),
|
||||||
),
|
),
|
||||||
|
elevation: WidgetStateProperty.all(elevation ?? 0),
|
||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: height ?? 50,
|
height: height ?? 50,
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
|
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
class ToggleWidget extends StatelessWidget {
|
class CurtainToggle extends StatelessWidget {
|
||||||
final bool value;
|
final bool value;
|
||||||
final String code;
|
final String code;
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
final String label;
|
final String label;
|
||||||
|
final Null Function(dynamic value) onChanged;
|
||||||
|
|
||||||
const ToggleWidget({
|
const CurtainToggle({
|
||||||
super.key,
|
super.key,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.code,
|
required this.code,
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
required this.label,
|
required this.label,
|
||||||
|
required this.onChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -37,30 +37,23 @@ class ToggleWidget extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ClipOval(
|
ClipOval(
|
||||||
child: Container(
|
child: Container(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.lightPulp,
|
Assets.curtainIcon,
|
||||||
width: 60,
|
width: 60,
|
||||||
height: 60,
|
height: 60,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 35,
|
width: 35,
|
||||||
child: CupertinoSwitch(
|
child: CupertinoSwitch(
|
||||||
value: value,
|
value: value,
|
||||||
activeColor: ColorsManager.dialogBlueTitle,
|
activeColor: ColorsManager.dialogBlueTitle,
|
||||||
onChanged: (newValue) {
|
onChanged: onChanged,
|
||||||
context.read<LivingRoomBloc>().add(
|
|
||||||
LivingRoomControl(
|
|
||||||
deviceId: deviceId,
|
|
||||||
code: code,
|
|
||||||
value: newValue,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
|
||||||
|
|
||||||
Future<void> showCustomDialog({
|
Future<void> showCustomDialog({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
@ -13,7 +12,7 @@ Future<void> showCustomDialog({
|
|||||||
double? iconWidth,
|
double? iconWidth,
|
||||||
VoidCallback? onOkPressed,
|
VoidCallback? onOkPressed,
|
||||||
bool barrierDismissible = false,
|
bool barrierDismissible = false,
|
||||||
required actions,
|
required List<Widget> actions,
|
||||||
}) {
|
}) {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -21,59 +20,43 @@ Future<void> showCustomDialog({
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
height: dialogHeight ?? size.height * 0.15,
|
height: dialogHeight ?? size.height * 0.15,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if (iconPath != null)
|
if (iconPath != null)
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
iconPath,
|
iconPath,
|
||||||
height: iconHeight ?? 35,
|
height: iconHeight ?? 35,
|
||||||
width: iconWidth ?? 35,
|
width: iconWidth ?? 35,
|
||||||
),
|
),
|
||||||
if (title != null)
|
if (title != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.headlineLarge!
|
||||||
|
.copyWith(fontSize: 20, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
|
),
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
message,
|
||||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.black),
|
||||||
fontSize: 20,
|
textAlign: TextAlign.center,
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: Colors.black),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
if (widget != null) Expanded(child: widget)
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
],
|
||||||
child: Text(
|
|
||||||
message,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: Colors.black),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if(widget!=null)
|
|
||||||
Expanded(child:widget)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
actionsAlignment: MainAxisAlignment.center,
|
|
||||||
actions: <Widget>[
|
|
||||||
TextButton(
|
|
||||||
onPressed: onOkPressed ?? () => Navigator.of(context).pop(),
|
|
||||||
child: Text(
|
|
||||||
'OK',
|
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
fontSize: 16),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
actionsAlignment: MainAxisAlignment.center,
|
||||||
);
|
actions: actions);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,13 @@ class DynamicTable extends StatefulWidget {
|
|||||||
final void Function(int, bool, dynamic)? onRowSelected;
|
final void Function(int, bool, dynamic)? onRowSelected;
|
||||||
final List<String>? initialSelectedIds;
|
final List<String>? initialSelectedIds;
|
||||||
final int uuidIndex;
|
final int uuidIndex;
|
||||||
|
final Function(dynamic selectedRows)? onSelectionChanged;
|
||||||
const DynamicTable({
|
const DynamicTable({
|
||||||
super.key,
|
super.key,
|
||||||
required this.headers,
|
required this.headers,
|
||||||
required this.data,
|
required this.data,
|
||||||
required this.size,
|
required this.size,
|
||||||
this.tableName,
|
this.tableName,
|
||||||
required this.isEmpty,
|
required this.isEmpty,
|
||||||
required this.withCheckBox,
|
required this.withCheckBox,
|
||||||
required this.withSelectAll,
|
required this.withSelectAll,
|
||||||
@ -32,6 +33,7 @@ class DynamicTable extends StatefulWidget {
|
|||||||
this.onRowSelected,
|
this.onRowSelected,
|
||||||
this.initialSelectedIds,
|
this.initialSelectedIds,
|
||||||
required this.uuidIndex,
|
required this.uuidIndex,
|
||||||
|
this.onSelectionChanged,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -39,7 +41,7 @@ class DynamicTable extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DynamicTableState extends State<DynamicTable> {
|
class _DynamicTableState extends State<DynamicTable> {
|
||||||
late List<bool> _selected;
|
late List<bool> _selectedRows;
|
||||||
bool _selectAll = false;
|
bool _selectAll = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -51,40 +53,30 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
@override
|
@override
|
||||||
void didUpdateWidget(DynamicTable oldWidget) {
|
void didUpdateWidget(DynamicTable oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (oldWidget.data != widget.data) {
|
if (oldWidget.data.length != widget.data.length) {
|
||||||
_initializeSelection();
|
_initializeSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _initializeSelection() {
|
void _initializeSelection() {
|
||||||
_selected = List<bool>.generate(widget.data.length, (index) {
|
_selectedRows = List<bool>.filled(widget.data.length, false);
|
||||||
// Check if the initialSelectedIds contains the deviceUuid
|
_selectAll = false;
|
||||||
// uuidIndex is the index of the column containing the deviceUuid
|
|
||||||
final deviceUuid = widget.data[index][widget.uuidIndex];
|
|
||||||
return widget.initialSelectedIds != null &&
|
|
||||||
widget.initialSelectedIds!.contains(deviceUuid);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggleRowSelection(int index) {
|
|
||||||
setState(() {
|
|
||||||
_selected[index] = !_selected[index];
|
|
||||||
|
|
||||||
if (widget.onRowSelected != null) {
|
|
||||||
widget.onRowSelected!(index, _selected[index], widget.data[index]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void _toggleSelectAll(bool? value) {
|
void _toggleSelectAll(bool? value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectAll = value ?? false;
|
_selectAll = value ?? false;
|
||||||
_selected = List<bool>.filled(widget.data.length, _selectAll);
|
_selectedRows = List<bool>.filled(widget.data.length, _selectAll);
|
||||||
if (widget.selectAll != null) {
|
|
||||||
widget.selectAll!(_selectAll);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
widget.onSelectionChanged?.call(_selectedRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _toggleRowSelection(int index) {
|
||||||
|
setState(() {
|
||||||
|
_selectedRows[index] = !_selectedRows[index];
|
||||||
|
_selectAll = _selectedRows.every((isSelected) => isSelected);
|
||||||
|
});
|
||||||
|
widget.onSelectionChanged?.call(_selectedRows);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -125,7 +117,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
// no password
|
// no password
|
||||||
widget.tableName=='AccessManagement'? 'No Password ' : 'No Devices',
|
widget.tableName == 'AccessManagement'
|
||||||
|
? 'No Password '
|
||||||
|
: 'No Devices',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
@ -178,8 +172,10 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
value: _selected.every((element) => element == true),
|
value: _selectAll,
|
||||||
onChanged:widget.withSelectAll?_toggleSelectAll:null,
|
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||||
|
? _toggleSelectAll
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -200,7 +196,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
value: _selected[index],
|
value: _selectedRows[index],
|
||||||
onChanged: (bool? value) {
|
onChanged: (bool? value) {
|
||||||
_toggleRowSelection(index);
|
_toggleRowSelection(index);
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HourPickerDialog extends StatefulWidget {
|
class HourPickerDialog extends StatefulWidget {
|
||||||
final TimeOfDay initialTime;
|
final TimeOfDay initialTime;
|
||||||
|
|
||||||
const HourPickerDialog({super.key, required this.initialTime});
|
const HourPickerDialog({super.key, required this.initialTime});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -9,70 +12,50 @@ class HourPickerDialog extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HourPickerDialogState extends State<HourPickerDialog> {
|
class _HourPickerDialogState extends State<HourPickerDialog> {
|
||||||
late int _selectedHour;
|
late String selectedHour;
|
||||||
bool _isPm = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedHour = widget.initialTime.hour > 12
|
// Initialize the selectedHour with the initial time passed to the dialog
|
||||||
? widget.initialTime.hour - 12
|
selectedHour = widget.initialTime.hour.toString().padLeft(2, '0') + ':00';
|
||||||
: widget.initialTime.hour;
|
|
||||||
_isPm = widget.initialTime.period == DayPeriod.pm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Select Hour'),
|
title: const Text('Select Hour'),
|
||||||
content: Row(
|
content: DropdownButton<String>(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
value: selectedHour, // Show the currently selected hour
|
||||||
children: [
|
items: List.generate(24, (index) {
|
||||||
DropdownButton<int>(
|
String hour = index.toString().padLeft(2, '0');
|
||||||
value: _selectedHour,
|
return DropdownMenuItem<String>(
|
||||||
items: List.generate(12, (index) {
|
value: '$hour:00',
|
||||||
int displayHour = index + 1;
|
child: Text('$hour:00'),
|
||||||
return DropdownMenuItem(
|
);
|
||||||
value: displayHour,
|
}),
|
||||||
child: Text(displayHour.toString()),
|
onChanged: (String? newValue) {
|
||||||
);
|
if (newValue != null) {
|
||||||
}),
|
setState(() {
|
||||||
onChanged: (value) {
|
selectedHour = newValue; // Update the selected hour without closing the dialog
|
||||||
setState(() {
|
});
|
||||||
_selectedHour = value!;
|
}
|
||||||
});
|
},
|
||||||
},
|
|
||||||
),
|
|
||||||
SizedBox(width: 16.0),
|
|
||||||
DropdownButton<bool>(
|
|
||||||
value: _isPm,
|
|
||||||
items: const [
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: false,
|
|
||||||
child: Text('AM'),
|
|
||||||
),
|
|
||||||
DropdownMenuItem(
|
|
||||||
value: true,
|
|
||||||
child: Text('PM'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() {
|
|
||||||
_isPm = value!;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(null),
|
onPressed: () => Navigator.of(context).pop(null), // Close the dialog without selection
|
||||||
child: const Text('Cancel'),
|
child: const Text('Cancel'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
int hour = _isPm ? _selectedHour + 12 : _selectedHour;
|
// Close the dialog and return the selected time
|
||||||
Navigator.of(context).pop(TimeOfDay(hour: hour, minute: 0));
|
Navigator.of(context).pop(
|
||||||
|
TimeOfDay(
|
||||||
|
hour: int.parse(selectedHour.split(':')[0]),
|
||||||
|
minute: 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
@ -86,6 +69,7 @@ Future<TimeOfDay?> showHourPicker({
|
|||||||
required TimeOfDay initialTime,
|
required TimeOfDay initialTime,
|
||||||
}) {
|
}) {
|
||||||
return showDialog<TimeOfDay>(
|
return showDialog<TimeOfDay>(
|
||||||
|
barrierDismissible: false,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => HourPickerDialog(initialTime: initialTime),
|
builder: (context) => HourPickerDialog(initialTime: initialTime),
|
||||||
);
|
);
|
||||||
|
@ -14,12 +14,15 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
|
AcBloc({required this.deviceId}) : super(AcsInitialState()) {
|
||||||
on<AcFetchDeviceStatus>(_onFetchAcStatus);
|
on<AcFetchDeviceStatusEvent>(_onFetchAcStatus);
|
||||||
on<AcControl>(_onAcControl);
|
on<AcFetchBatchStatusEvent>(_onFetchAcBatchStatus);
|
||||||
|
on<AcControlEvent>(_onAcControl);
|
||||||
|
on<AcBatchControlEvent>(_onAcBatchControl);
|
||||||
|
on<AcFactoryResetEvent>(_onFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onFetchAcStatus(
|
FutureOr<void> _onFetchAcStatus(
|
||||||
AcFetchDeviceStatus event, Emitter<AcsState> emit) async {
|
AcFetchDeviceStatusEvent event, Emitter<AcsState> emit) async {
|
||||||
emit(AcsLoadingState());
|
emit(AcsLoadingState());
|
||||||
try {
|
try {
|
||||||
final status =
|
final status =
|
||||||
@ -31,7 +34,8 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onAcControl(AcControl event, Emitter<AcsState> emit) async {
|
FutureOr<void> _onAcControl(
|
||||||
|
AcControlEvent event, Emitter<AcsState> emit) async {
|
||||||
final oldValue = _getValueByCode(event.code);
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
_updateLocalValue(event.code, event.value, emit);
|
_updateLocalValue(event.code, event.value, emit);
|
||||||
@ -39,6 +43,7 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
await _runDebounce(
|
await _runDebounce(
|
||||||
|
isBatch: false,
|
||||||
deviceId: event.deviceId,
|
deviceId: event.deviceId,
|
||||||
code: event.code,
|
code: event.code,
|
||||||
value: event.value,
|
value: event.value,
|
||||||
@ -48,27 +53,43 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runDebounce({
|
Future<void> _runDebounce({
|
||||||
required String deviceId,
|
required dynamic deviceId,
|
||||||
required String code,
|
required String code,
|
||||||
required dynamic value,
|
required dynamic value,
|
||||||
required dynamic oldValue,
|
required dynamic oldValue,
|
||||||
required Emitter<AcsState> emit,
|
required Emitter<AcsState> emit,
|
||||||
|
required bool isBatch,
|
||||||
}) async {
|
}) async {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
}
|
}
|
||||||
_timer = Timer(const Duration(seconds: 1), () async {
|
_timer = Timer(const Duration(seconds: 1), () async {
|
||||||
try {
|
try {
|
||||||
final response = await DevicesManagementApi()
|
late bool response;
|
||||||
.deviceControl(deviceId, Status(code: code, value: value));
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e is DioException && e.response != null) {
|
if (e is DioException && e.response != null) {
|
||||||
debugPrint('Error response: ${e.response?.data}');
|
debugPrint('Error response: ${e.response?.data}');
|
||||||
}
|
}
|
||||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -77,7 +98,6 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
|
String deviceId, String code, dynamic oldValue, Emitter<AcsState> emit) {
|
||||||
_updateLocalValue(code, oldValue, emit);
|
_updateLocalValue(code, oldValue, emit);
|
||||||
emit(ACStatusLoaded(deviceStatus));
|
emit(ACStatusLoaded(deviceStatus));
|
||||||
emit(const AcsFailedState(error: 'Failed to control the device.'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
|
void _updateLocalValue(String code, dynamic value, Emitter<AcsState> emit) {
|
||||||
@ -133,4 +153,54 @@ class AcBloc extends Bloc<AcsEvent, AcsState> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchAcBatchStatus(
|
||||||
|
AcFetchBatchStatusEvent event, Emitter<AcsState> emit) async {
|
||||||
|
emit(AcsLoadingState());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus =
|
||||||
|
AcStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||||
|
emit(ACStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(AcsFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onAcBatchControl(
|
||||||
|
AcBatchControlEvent event, Emitter<AcsState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value, emit);
|
||||||
|
|
||||||
|
emit(ACStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
isBatch: true,
|
||||||
|
deviceId: event.devicesIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
AcFactoryResetEvent event, Emitter<AcsState> emit) async {
|
||||||
|
emit(AcsLoadingState());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryResetModel,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const AcsFailedState(error: 'Failed'));
|
||||||
|
} else {
|
||||||
|
add(AcFetchDeviceStatusEvent(event.deviceId));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(AcsFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
sealed class AcsEvent extends Equatable {
|
sealed class AcsEvent extends Equatable {
|
||||||
const AcsEvent();
|
const AcsEvent();
|
||||||
@ -7,21 +8,30 @@ sealed class AcsEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AcFetchDeviceStatus extends AcsEvent {
|
class AcFetchDeviceStatusEvent extends AcsEvent {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const AcFetchDeviceStatus(this.deviceId);
|
const AcFetchDeviceStatusEvent(this.deviceId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId];
|
List<Object> get props => [deviceId];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AcControl extends AcsEvent {
|
class AcFetchBatchStatusEvent extends AcsEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
const AcFetchBatchStatusEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class AcControlEvent extends AcsEvent {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
final String code;
|
final String code;
|
||||||
final dynamic value;
|
final dynamic value;
|
||||||
|
|
||||||
const AcControl({
|
const AcControlEvent({
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
required this.code,
|
required this.code,
|
||||||
required this.value,
|
required this.value,
|
||||||
@ -30,3 +40,31 @@ class AcControl extends AcsEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId, code, value];
|
List<Object> get props => [deviceId, code, value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AcBatchControlEvent extends AcsEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
final String code;
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
const AcBatchControlEvent({
|
||||||
|
required this.devicesIds,
|
||||||
|
required this.code,
|
||||||
|
required this.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class AcFactoryResetEvent extends AcsEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryResetModel;
|
||||||
|
|
||||||
|
const AcFactoryResetEvent({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.factoryResetModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryResetModel];
|
||||||
|
}
|
||||||
|
@ -22,6 +22,16 @@ class ACStatusLoaded extends AcsState {
|
|||||||
List<Object> get props => [status, timestamp];
|
List<Object> get props => [status, timestamp];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AcBatchStatusLoaded extends AcsState {
|
||||||
|
final AcStatusModel status;
|
||||||
|
final DateTime timestamp;
|
||||||
|
|
||||||
|
AcBatchStatusLoaded(this.status) : timestamp = DateTime.now();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status, timestamp];
|
||||||
|
}
|
||||||
|
|
||||||
class AcsFailedState extends AcsState {
|
class AcsFailedState extends AcsState {
|
||||||
final String error;
|
final String error;
|
||||||
|
|
||||||
|
@ -40,16 +40,16 @@ class AcStatusModel {
|
|||||||
acSwitch = status.value ?? false;
|
acSwitch = status.value ?? false;
|
||||||
break;
|
break;
|
||||||
case 'mode':
|
case 'mode':
|
||||||
mode = status.value ?? 'cold'; // default to 'cold' if null
|
mode = status.value ?? 'cold';
|
||||||
break;
|
break;
|
||||||
case 'temp_set':
|
case 'temp_set':
|
||||||
tempSet = status.value ?? 210; // default value if null
|
tempSet = status.value ?? 210;
|
||||||
break;
|
break;
|
||||||
case 'temp_current':
|
case 'temp_current':
|
||||||
currentTemp = status.value ?? 210; // default value if null
|
currentTemp = status.value ?? 210;
|
||||||
break;
|
break;
|
||||||
case 'level':
|
case 'level':
|
||||||
fanSpeeds = status.value ?? 'low'; // default value if null
|
fanSpeeds = status.value ?? 'low';
|
||||||
break;
|
break;
|
||||||
case 'child_lock':
|
case 'child_lock':
|
||||||
childLock = status.value ?? false;
|
childLock = status.value ?? false;
|
||||||
|
166
lib/pages/device_managment/ac/view/ac_device_batch_control.dart
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_ac_mode.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_current_temp.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/view/batch_control_list/batch_fan_speed.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.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';
|
||||||
|
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 AcDeviceBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const AcDeviceBatchControlView({super.key, required this.devicesIds});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => AcBloc(deviceId: devicesIds.first)
|
||||||
|
..add(AcFetchBatchStatusEvent(devicesIds)),
|
||||||
|
child: BlocBuilder<AcBloc, AcsState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is ACStatusLoaded) {
|
||||||
|
return GridView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: isLarge || isExtraLarge
|
||||||
|
? 3
|
||||||
|
: isMedium
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
|
mainAxisExtent: 140,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
ToggleWidget(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
code: 'switch',
|
||||||
|
value: state.status.acSwitch,
|
||||||
|
label: 'ThermoState',
|
||||||
|
icon: Assets.ac,
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<AcBloc>().add(AcBatchControlEvent(
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
code: 'switch',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
BatchCurrentTemp(
|
||||||
|
currentTemp: state.status.currentTemp,
|
||||||
|
tempSet: state.status.tempSet,
|
||||||
|
code: 'temp_set',
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
),
|
||||||
|
BatchAcMode(
|
||||||
|
value: state.status.acMode,
|
||||||
|
code: 'mode',
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
),
|
||||||
|
BatchFanSpeedControl(
|
||||||
|
value: state.status.acFanSpeed,
|
||||||
|
code: 'level',
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
label: '',
|
||||||
|
labelWidget: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.remove,
|
||||||
|
size: 28,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 28,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
value: false,
|
||||||
|
code: 'ac_schedule',
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
icon: Assets.acSchedule,
|
||||||
|
onChange: (value) {},
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
code: 'child_lock',
|
||||||
|
value: state.status.childLock,
|
||||||
|
label: 'Child Lock',
|
||||||
|
icon: state.status.childLock ? Assets.unlock : Assets.acLock,
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<AcBloc>().add(AcBatchControlEvent(
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
code: 'child_lock',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 5),
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<AcBloc>().add(AcFactoryResetEvent(
|
||||||
|
deviceId: state.status.uuid,
|
||||||
|
factoryResetModel:
|
||||||
|
FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else if (state is AcsLoadingState) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,15 +4,17 @@ import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
|||||||
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_state.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/ac_mode.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/ac_mode.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/ac_toggle.dart';
|
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/current_temp.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/current_temp.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/fan_speed.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/view/control_list/fan_speed.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/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/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||||
const AcDeviceControl({super.key, required this.device});
|
const AcDeviceControlsView({super.key, required this.device});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
final isMedium = isMediumScreenSize(context);
|
final isMedium = isMediumScreenSize(context);
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => AcBloc(deviceId: device.uuid!)
|
create: (context) => AcBloc(deviceId: device.uuid!)
|
||||||
..add(AcFetchDeviceStatus(device.uuid!)),
|
..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
||||||
child: BlocBuilder<AcBloc, AcsState>(
|
child: BlocBuilder<AcBloc, AcsState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is ACStatusLoaded) {
|
if (state is ACStatusLoaded) {
|
||||||
@ -42,10 +44,21 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
mainAxisSpacing: 12,
|
mainAxisSpacing: 12,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
AcToggle(
|
ToggleWidget(
|
||||||
|
label: 'Thermostat',
|
||||||
value: state.status.acSwitch,
|
value: state.status.acSwitch,
|
||||||
code: 'switch',
|
code: 'switch',
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
|
icon: Assets.ac,
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<AcBloc>().add(
|
||||||
|
AcControlEvent(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
code: 'switch',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
CurrentTemp(
|
CurrentTemp(
|
||||||
currentTemp: state.status.currentTemp,
|
currentTemp: state.status.currentTemp,
|
||||||
@ -63,19 +76,79 @@ class AcDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
code: 'level',
|
code: 'level',
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
),
|
),
|
||||||
AcToggle(
|
ToggleWidget(
|
||||||
value: state.status.childLock,
|
label: '',
|
||||||
code: 'child_lock',
|
labelWidget: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.remove,
|
||||||
|
size: 28,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 28,
|
||||||
|
color: ColorsManager.greyColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
value: false,
|
||||||
|
code: 'ac_schedule',
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
description: 'Child Lock',
|
icon: Assets.acSchedule,
|
||||||
icon: Assets.childLock,
|
onChange: (value) {},
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
code: 'child_lock',
|
||||||
|
value: state.status.childLock,
|
||||||
|
label: 'Child Lock',
|
||||||
|
icon: state.status.childLock ? Assets.unlock : Assets.acLock,
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<AcBloc>().add(
|
||||||
|
AcControlEvent(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
code: 'child_lock',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else if (state is AcsLoadingState) {
|
} else if (state is AcsLoadingState) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: Text('Error fetching status'));
|
return const Center(child: Text('Error fetching status'));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
||||||
|
|
||||||
|
class BatchAcMode extends StatelessWidget {
|
||||||
|
const BatchAcMode({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.code,
|
||||||
|
required this.devicesIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
final TempModes value;
|
||||||
|
final String code;
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||||
|
border: Border.all(color: ColorsManager.boxDivider),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_buildIconContainer(context, TempModes.cold, Assets.freezing,
|
||||||
|
value == TempModes.cold),
|
||||||
|
_buildIconContainer(
|
||||||
|
context, TempModes.hot, Assets.acSun, value == TempModes.hot),
|
||||||
|
_buildIconContainer(context, TempModes.wind, Assets.acAirConditioner,
|
||||||
|
value == TempModes.wind),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIconContainer(
|
||||||
|
BuildContext context, TempModes mode, String assetPath, bool isSelected) {
|
||||||
|
return Flexible(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
context.read<AcBloc>().add(
|
||||||
|
AcBatchControlEvent(
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
code: code,
|
||||||
|
value: mode.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
child: ClipOval(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
assetPath,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/celciuse_symbol.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||||
|
|
||||||
|
class BatchCurrentTemp extends StatefulWidget {
|
||||||
|
const BatchCurrentTemp({
|
||||||
|
super.key,
|
||||||
|
required this.code,
|
||||||
|
required this.devicesIds,
|
||||||
|
required this.currentTemp,
|
||||||
|
required this.tempSet,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String code;
|
||||||
|
final List<String> devicesIds;
|
||||||
|
final int currentTemp;
|
||||||
|
final int tempSet;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<BatchCurrentTemp> createState() => _CurrentTempState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CurrentTempState extends State<BatchCurrentTemp> {
|
||||||
|
late double _adjustedValue;
|
||||||
|
Timer? _debounce;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_adjustedValue = _initialAdjustedValue(widget.tempSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _initialAdjustedValue(dynamic value) {
|
||||||
|
if (value is int || value is double) {
|
||||||
|
double doubleValue = value.toDouble();
|
||||||
|
return doubleValue > 99 ? doubleValue / 10 : doubleValue;
|
||||||
|
} else {
|
||||||
|
throw ArgumentError('Invalid value type: Expected int or double');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onValueChanged(double newValue) {
|
||||||
|
if (_debounce?.isActive ?? false) {
|
||||||
|
_debounce?.cancel();
|
||||||
|
}
|
||||||
|
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||||
|
context.read<AcBloc>().add(
|
||||||
|
AcBatchControlEvent(
|
||||||
|
devicesIds: widget.devicesIds,
|
||||||
|
code: widget.code,
|
||||||
|
value: (newValue * 10).toInt(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_debounce?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||||
|
border: Border.all(color: ColorsManager.boxDivider),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Current Temperature',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(color: Colors.grey),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 5,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
(widget.currentTemp > 99
|
||||||
|
? widget.currentTemp / 10
|
||||||
|
: widget.currentTemp)
|
||||||
|
.toString(),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(color: Colors.grey),
|
||||||
|
),
|
||||||
|
const CelsiusSymbol(
|
||||||
|
color: Colors.grey,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
IncrementDecrementWidget(
|
||||||
|
value: _adjustedValue.toString(),
|
||||||
|
description: '°C',
|
||||||
|
descriptionColor: ColorsManager.dialogBlueTitle,
|
||||||
|
onIncrement: () {
|
||||||
|
if (_adjustedValue < 30) {
|
||||||
|
setState(() {
|
||||||
|
_adjustedValue = _adjustedValue + 0.5;
|
||||||
|
});
|
||||||
|
_onValueChanged(_adjustedValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDecrement: () {
|
||||||
|
if (_adjustedValue > 20) {
|
||||||
|
setState(() {
|
||||||
|
_adjustedValue = _adjustedValue - 0.5;
|
||||||
|
});
|
||||||
|
_onValueChanged(_adjustedValue);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/model/ac_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.dart';
|
||||||
|
|
||||||
|
class BatchFanSpeedControl extends StatelessWidget {
|
||||||
|
const BatchFanSpeedControl({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.code,
|
||||||
|
required this.devicesIds,
|
||||||
|
});
|
||||||
|
|
||||||
|
final FanSpeeds value;
|
||||||
|
final String code;
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||||
|
border: Border.all(color: ColorsManager.boxDivider),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
runSpacing: 8,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto,
|
||||||
|
value == FanSpeeds.auto),
|
||||||
|
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow,
|
||||||
|
value == FanSpeeds.low),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Wrap(
|
||||||
|
runSpacing: 8,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle,
|
||||||
|
value == FanSpeeds.middle),
|
||||||
|
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh,
|
||||||
|
value == FanSpeeds.high),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
|
||||||
|
String assetPath, bool isSelected) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
context.read<AcBloc>().add(
|
||||||
|
AcBatchControlEvent(
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
code: code,
|
||||||
|
value: speed.name,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected ? Colors.blue : Colors.transparent,
|
||||||
|
width: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: ClipOval(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
assetPath,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -49,7 +49,7 @@ class AcMode extends StatelessWidget {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<AcBloc>().add(
|
context.read<AcBloc>().add(
|
||||||
AcControl(
|
AcControlEvent(
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
code: code,
|
code: code,
|
||||||
value: mode.name,
|
value: mode.name,
|
||||||
|
@ -39,16 +39,21 @@ class AcToggle extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClipOval(
|
Container(
|
||||||
child: Container(
|
width: 60,
|
||||||
color: ColorsManager.whiteColors,
|
height: 60,
|
||||||
child: SvgPicture.asset(
|
decoration: const BoxDecoration(
|
||||||
icon ?? Assets.acDevice,
|
shape: BoxShape.circle,
|
||||||
width: 60,
|
color: ColorsManager.whiteColors,
|
||||||
height: 60,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
),
|
),
|
||||||
)),
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: ClipOval(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon ?? Assets.lightPulp,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: 20,
|
height: 20,
|
||||||
width: 35,
|
width: 35,
|
||||||
@ -57,7 +62,7 @@ class AcToggle extends StatelessWidget {
|
|||||||
value: value,
|
value: value,
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
context.read<AcBloc>().add(
|
context.read<AcBloc>().add(
|
||||||
AcControl(
|
AcControlEvent(
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
code: code,
|
code: code,
|
||||||
value: newValue,
|
value: newValue,
|
||||||
|
@ -50,7 +50,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
|||||||
}
|
}
|
||||||
_debounce = Timer(const Duration(milliseconds: 500), () {
|
_debounce = Timer(const Duration(milliseconds: 500), () {
|
||||||
context.read<AcBloc>().add(
|
context.read<AcBloc>().add(
|
||||||
AcControl(
|
AcControlEvent(
|
||||||
deviceId: widget.deviceId,
|
deviceId: widget.deviceId,
|
||||||
code: widget.code,
|
code: widget.code,
|
||||||
value: (newValue * 10).toInt(),
|
value: (newValue * 10).toInt(),
|
||||||
@ -118,7 +118,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
|||||||
onIncrement: () {
|
onIncrement: () {
|
||||||
if (_adjustedValue < 30) {
|
if (_adjustedValue < 30) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_adjustedValue++;
|
_adjustedValue = _adjustedValue + 0.5;
|
||||||
});
|
});
|
||||||
_onValueChanged(_adjustedValue);
|
_onValueChanged(_adjustedValue);
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
|||||||
onDecrement: () {
|
onDecrement: () {
|
||||||
if (_adjustedValue > 20) {
|
if (_adjustedValue > 20) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_adjustedValue--;
|
_adjustedValue = _adjustedValue - 0.5;
|
||||||
});
|
});
|
||||||
_onValueChanged(_adjustedValue);
|
_onValueChanged(_adjustedValue);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ class FanSpeedControl extends StatelessWidget {
|
|||||||
value == FanSpeeds.low),
|
value == FanSpeeds.low),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
Wrap(
|
Wrap(
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
@ -60,7 +61,7 @@ class FanSpeedControl extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.read<AcBloc>().add(
|
context.read<AcBloc>().add(
|
||||||
AcControl(
|
AcControlEvent(
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
code: code,
|
code: code,
|
||||||
value: speed.name,
|
value: speed.name,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
@ -14,6 +14,8 @@ class DeviceManagementBloc
|
|||||||
int _offlineCount = 0;
|
int _offlineCount = 0;
|
||||||
int _lowBatteryCount = 0;
|
int _lowBatteryCount = 0;
|
||||||
List<AllDevicesModel> _selectedDevices = [];
|
List<AllDevicesModel> _selectedDevices = [];
|
||||||
|
List<AllDevicesModel> _filteredDevices = [];
|
||||||
|
String productName = '';
|
||||||
|
|
||||||
DeviceManagementBloc() : super(DeviceManagementInitial()) {
|
DeviceManagementBloc() : super(DeviceManagementInitial()) {
|
||||||
on<FetchDevices>(_onFetchDevices);
|
on<FetchDevices>(_onFetchDevices);
|
||||||
@ -21,6 +23,9 @@ class DeviceManagementBloc
|
|||||||
on<SelectedFilterChanged>(_onSelectedFilterChanged);
|
on<SelectedFilterChanged>(_onSelectedFilterChanged);
|
||||||
on<SearchDevices>(_onSearchDevices);
|
on<SearchDevices>(_onSearchDevices);
|
||||||
on<SelectDevice>(_onSelectDevice);
|
on<SelectDevice>(_onSelectDevice);
|
||||||
|
on<ResetFilters>(_onResetFilters);
|
||||||
|
on<ResetSelectedDevices>(_onResetSelectedDevices);
|
||||||
|
on<UpdateSelection>(_onUpdateSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onFetchDevices(
|
Future<void> _onFetchDevices(
|
||||||
@ -30,6 +35,7 @@ class DeviceManagementBloc
|
|||||||
final devices = await DevicesManagementApi().fetchDevices();
|
final devices = await DevicesManagementApi().fetchDevices();
|
||||||
_selectedDevices.clear();
|
_selectedDevices.clear();
|
||||||
_devices = devices;
|
_devices = devices;
|
||||||
|
_filteredDevices = devices;
|
||||||
_calculateDeviceCounts();
|
_calculateDeviceCounts();
|
||||||
emit(DeviceManagementLoaded(
|
emit(DeviceManagementLoaded(
|
||||||
devices: devices,
|
devices: devices,
|
||||||
@ -38,6 +44,7 @@ class DeviceManagementBloc
|
|||||||
offlineCount: _offlineCount,
|
offlineCount: _offlineCount,
|
||||||
lowBatteryCount: _lowBatteryCount,
|
lowBatteryCount: _lowBatteryCount,
|
||||||
selectedDevice: null,
|
selectedDevice: null,
|
||||||
|
isControlButtonEnabled: false,
|
||||||
));
|
));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(DeviceManagementInitial());
|
emit(DeviceManagementInitial());
|
||||||
@ -45,9 +52,9 @@ class DeviceManagementBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onFilterDevices(
|
void _onFilterDevices(
|
||||||
FilterDevices event, Emitter<DeviceManagementState> emit) {
|
FilterDevices event, Emitter<DeviceManagementState> emit) async {
|
||||||
if (_devices.isNotEmpty) {
|
if (_devices.isNotEmpty) {
|
||||||
final filteredDevices = _devices.where((device) {
|
_filteredDevices = List.from(_devices.where((device) {
|
||||||
switch (event.filter) {
|
switch (event.filter) {
|
||||||
case 'Online':
|
case 'Online':
|
||||||
return device.online == true;
|
return device.online == true;
|
||||||
@ -58,15 +65,64 @@ class DeviceManagementBloc
|
|||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}).toList();
|
}).toList());
|
||||||
|
|
||||||
emit(DeviceManagementFiltered(
|
emit(DeviceManagementFiltered(
|
||||||
filteredDevices: filteredDevices,
|
filteredDevices: _filteredDevices,
|
||||||
selectedIndex: _selectedIndex,
|
selectedIndex: _selectedIndex,
|
||||||
onlineCount: _onlineCount,
|
onlineCount: _onlineCount,
|
||||||
offlineCount: _offlineCount,
|
offlineCount: _offlineCount,
|
||||||
lowBatteryCount: _lowBatteryCount,
|
lowBatteryCount: _lowBatteryCount,
|
||||||
selectedDevice:
|
selectedDevice: _selectedDevices.isNotEmpty ? _selectedDevices : null,
|
||||||
_selectedDevices.isNotEmpty ? _selectedDevices.first : null,
|
isControlButtonEnabled: _selectedDevices.isNotEmpty,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (productName.isNotEmpty) {
|
||||||
|
add(SearchDevices(productName: productName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onResetFilters(
|
||||||
|
ResetFilters event, Emitter<DeviceManagementState> emit) async {
|
||||||
|
productName = '';
|
||||||
|
_selectedDevices.clear();
|
||||||
|
_filteredDevices = List.from(_devices);
|
||||||
|
_selectedIndex = 0;
|
||||||
|
emit(DeviceManagementLoaded(
|
||||||
|
devices: _devices,
|
||||||
|
selectedIndex: 0,
|
||||||
|
onlineCount: _onlineCount,
|
||||||
|
offlineCount: _offlineCount,
|
||||||
|
lowBatteryCount: _lowBatteryCount,
|
||||||
|
selectedDevice: null,
|
||||||
|
isControlButtonEnabled: false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onResetSelectedDevices(
|
||||||
|
ResetSelectedDevices event, Emitter<DeviceManagementState> emit) {
|
||||||
|
_selectedDevices.clear();
|
||||||
|
|
||||||
|
if (state is DeviceManagementLoaded) {
|
||||||
|
emit(DeviceManagementLoaded(
|
||||||
|
devices: _devices,
|
||||||
|
selectedIndex: _selectedIndex,
|
||||||
|
onlineCount: _onlineCount,
|
||||||
|
offlineCount: _offlineCount,
|
||||||
|
lowBatteryCount: _lowBatteryCount,
|
||||||
|
selectedDevice: null,
|
||||||
|
isControlButtonEnabled: false,
|
||||||
|
));
|
||||||
|
} else if (state is DeviceManagementFiltered) {
|
||||||
|
emit(DeviceManagementFiltered(
|
||||||
|
filteredDevices: (state as DeviceManagementFiltered).filteredDevices,
|
||||||
|
selectedIndex: _selectedIndex,
|
||||||
|
onlineCount: _onlineCount,
|
||||||
|
offlineCount: _offlineCount,
|
||||||
|
lowBatteryCount: _lowBatteryCount,
|
||||||
|
selectedDevice: null,
|
||||||
|
isControlButtonEnabled: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,7 +143,10 @@ class DeviceManagementBloc
|
|||||||
_selectedDevices.add(event.selectedDevice);
|
_selectedDevices.add(event.selectedDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isControlButtonEnabled = _selectedDevices.length == 1;
|
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices);
|
||||||
|
|
||||||
|
bool isControlButtonEnabled =
|
||||||
|
_checkIfControlButtonEnabled(clonedSelectedDevices);
|
||||||
|
|
||||||
if (state is DeviceManagementLoaded) {
|
if (state is DeviceManagementLoaded) {
|
||||||
emit(DeviceManagementLoaded(
|
emit(DeviceManagementLoaded(
|
||||||
@ -96,7 +155,9 @@ class DeviceManagementBloc
|
|||||||
onlineCount: _onlineCount,
|
onlineCount: _onlineCount,
|
||||||
offlineCount: _offlineCount,
|
offlineCount: _offlineCount,
|
||||||
lowBatteryCount: _lowBatteryCount,
|
lowBatteryCount: _lowBatteryCount,
|
||||||
selectedDevice: isControlButtonEnabled ? _selectedDevices.first : null,
|
selectedDevice:
|
||||||
|
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||||
|
isControlButtonEnabled: isControlButtonEnabled,
|
||||||
));
|
));
|
||||||
} else if (state is DeviceManagementFiltered) {
|
} else if (state is DeviceManagementFiltered) {
|
||||||
emit(DeviceManagementFiltered(
|
emit(DeviceManagementFiltered(
|
||||||
@ -105,11 +166,66 @@ class DeviceManagementBloc
|
|||||||
onlineCount: _onlineCount,
|
onlineCount: _onlineCount,
|
||||||
offlineCount: _offlineCount,
|
offlineCount: _offlineCount,
|
||||||
lowBatteryCount: _lowBatteryCount,
|
lowBatteryCount: _lowBatteryCount,
|
||||||
selectedDevice: isControlButtonEnabled ? _selectedDevices.first : null,
|
selectedDevice:
|
||||||
|
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||||
|
isControlButtonEnabled: isControlButtonEnabled,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onUpdateSelection(
|
||||||
|
UpdateSelection event, Emitter<DeviceManagementState> emit) {
|
||||||
|
List<AllDevicesModel> selectedDevices = [];
|
||||||
|
List<AllDevicesModel> devicesToSelectFrom = [];
|
||||||
|
|
||||||
|
if (state is DeviceManagementLoaded) {
|
||||||
|
devicesToSelectFrom = (state as DeviceManagementLoaded).devices;
|
||||||
|
} else if (state is DeviceManagementFiltered) {
|
||||||
|
devicesToSelectFrom = (state as DeviceManagementFiltered).filteredDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < event.selectedRows.length; i++) {
|
||||||
|
if (event.selectedRows[i]) {
|
||||||
|
selectedDevices.add(devicesToSelectFrom[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state is DeviceManagementLoaded) {
|
||||||
|
final loadedState = state as DeviceManagementLoaded;
|
||||||
|
emit(DeviceManagementLoaded(
|
||||||
|
devices: loadedState.devices,
|
||||||
|
selectedIndex: loadedState.selectedIndex,
|
||||||
|
onlineCount: loadedState.onlineCount,
|
||||||
|
offlineCount: loadedState.offlineCount,
|
||||||
|
lowBatteryCount: loadedState.lowBatteryCount,
|
||||||
|
selectedDevice: selectedDevices,
|
||||||
|
isControlButtonEnabled: _checkIfControlButtonEnabled(selectedDevices),
|
||||||
|
));
|
||||||
|
} else if (state is DeviceManagementFiltered) {
|
||||||
|
final filteredState = state as DeviceManagementFiltered;
|
||||||
|
emit(DeviceManagementFiltered(
|
||||||
|
filteredDevices: filteredState.filteredDevices,
|
||||||
|
selectedIndex: filteredState.selectedIndex,
|
||||||
|
onlineCount: filteredState.onlineCount,
|
||||||
|
offlineCount: filteredState.offlineCount,
|
||||||
|
lowBatteryCount: filteredState.lowBatteryCount,
|
||||||
|
selectedDevice: selectedDevices,
|
||||||
|
isControlButtonEnabled: _checkIfControlButtonEnabled(selectedDevices),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _checkIfControlButtonEnabled(List<AllDevicesModel> selectedDevices) {
|
||||||
|
if (selectedDevices.length > 1) {
|
||||||
|
final productTypes =
|
||||||
|
selectedDevices.map((device) => device.productType).toSet();
|
||||||
|
return productTypes.length == 1;
|
||||||
|
} else if (selectedDevices.length == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void _calculateDeviceCounts() {
|
void _calculateDeviceCounts() {
|
||||||
_onlineCount = _devices.where((device) => device.online == true).length;
|
_onlineCount = _devices.where((device) => device.online == true).length;
|
||||||
_offlineCount = _devices.where((device) => device.online == false).length;
|
_offlineCount = _devices.where((device) => device.online == false).length;
|
||||||
@ -134,21 +250,17 @@ class DeviceManagementBloc
|
|||||||
|
|
||||||
void _onSearchDevices(
|
void _onSearchDevices(
|
||||||
SearchDevices event, Emitter<DeviceManagementState> emit) {
|
SearchDevices event, Emitter<DeviceManagementState> emit) {
|
||||||
// If the search fields are all empty, restore the last filtered devices
|
|
||||||
if ((event.community == null || event.community!.isEmpty) &&
|
if ((event.community == null || event.community!.isEmpty) &&
|
||||||
(event.unitName == null || event.unitName!.isEmpty) &&
|
(event.unitName == null || event.unitName!.isEmpty) &&
|
||||||
(event.productName == null || event.productName!.isEmpty)) {
|
(event.productName == null || event.productName!.isEmpty)) {
|
||||||
// If the current state is filtered, re-emit the filtered state
|
productName = '';
|
||||||
if (state is DeviceManagementFiltered) {
|
if (state is DeviceManagementFiltered) {
|
||||||
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<AllDevicesModel> devicesToSearch = _devices;
|
productName = event.productName ?? '';
|
||||||
|
List<AllDevicesModel> devicesToSearch = _filteredDevices;
|
||||||
if (state is DeviceManagementFiltered) {
|
|
||||||
devicesToSearch = (state as DeviceManagementFiltered).filteredDevices;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (devicesToSearch.isNotEmpty) {
|
if (devicesToSearch.isNotEmpty) {
|
||||||
_selectedDevices.clear();
|
_selectedDevices.clear();
|
||||||
@ -192,6 +304,7 @@ class DeviceManagementBloc
|
|||||||
offlineCount: _offlineCount,
|
offlineCount: _offlineCount,
|
||||||
lowBatteryCount: _lowBatteryCount,
|
lowBatteryCount: _lowBatteryCount,
|
||||||
selectedDevice: null,
|
selectedDevice: null,
|
||||||
|
isControlButtonEnabled: false,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,3 +50,13 @@ class SelectDevice extends DeviceManagementEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [selectedDevice];
|
List<Object?> get props => [selectedDevice];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResetFilters extends DeviceManagementEvent {}
|
||||||
|
|
||||||
|
class ResetSelectedDevices extends DeviceManagementEvent {}
|
||||||
|
|
||||||
|
class UpdateSelection extends DeviceManagementEvent {
|
||||||
|
final List<bool> selectedRows;
|
||||||
|
|
||||||
|
const UpdateSelection(this.selectedRows);
|
||||||
|
}
|
||||||
|
@ -17,7 +17,8 @@ class DeviceManagementLoaded extends DeviceManagementState {
|
|||||||
final int onlineCount;
|
final int onlineCount;
|
||||||
final int offlineCount;
|
final int offlineCount;
|
||||||
final int lowBatteryCount;
|
final int lowBatteryCount;
|
||||||
final AllDevicesModel? selectedDevice;
|
final List<AllDevicesModel>? selectedDevice;
|
||||||
|
final bool isControlButtonEnabled;
|
||||||
|
|
||||||
const DeviceManagementLoaded({
|
const DeviceManagementLoaded({
|
||||||
required this.devices,
|
required this.devices,
|
||||||
@ -26,6 +27,7 @@ class DeviceManagementLoaded extends DeviceManagementState {
|
|||||||
required this.offlineCount,
|
required this.offlineCount,
|
||||||
required this.lowBatteryCount,
|
required this.lowBatteryCount,
|
||||||
this.selectedDevice,
|
this.selectedDevice,
|
||||||
|
required this.isControlButtonEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -35,7 +37,8 @@ class DeviceManagementLoaded extends DeviceManagementState {
|
|||||||
onlineCount,
|
onlineCount,
|
||||||
offlineCount,
|
offlineCount,
|
||||||
lowBatteryCount,
|
lowBatteryCount,
|
||||||
selectedDevice
|
selectedDevice,
|
||||||
|
isControlButtonEnabled
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +48,8 @@ class DeviceManagementFiltered extends DeviceManagementState {
|
|||||||
final int onlineCount;
|
final int onlineCount;
|
||||||
final int offlineCount;
|
final int offlineCount;
|
||||||
final int lowBatteryCount;
|
final int lowBatteryCount;
|
||||||
final AllDevicesModel? selectedDevice;
|
final List<AllDevicesModel>? selectedDevice;
|
||||||
|
final bool isControlButtonEnabled;
|
||||||
|
|
||||||
const DeviceManagementFiltered({
|
const DeviceManagementFiltered({
|
||||||
required this.filteredDevices,
|
required this.filteredDevices,
|
||||||
@ -54,6 +58,7 @@ class DeviceManagementFiltered extends DeviceManagementState {
|
|||||||
required this.offlineCount,
|
required this.offlineCount,
|
||||||
required this.lowBatteryCount,
|
required this.lowBatteryCount,
|
||||||
this.selectedDevice,
|
this.selectedDevice,
|
||||||
|
required this.isControlButtonEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -63,7 +68,8 @@ class DeviceManagementFiltered extends DeviceManagementState {
|
|||||||
onlineCount,
|
onlineCount,
|
||||||
offlineCount,
|
offlineCount,
|
||||||
lowBatteryCount,
|
lowBatteryCount,
|
||||||
selectedDevice
|
selectedDevice,
|
||||||
|
isControlButtonEnabled
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +1,163 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ac/view/ac_device_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ac/view/ac_device_control.dart';
|
import 'package:syncrow_web/pages/device_managment/ac/view/ac_device_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart';
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/view/ceiling_sensor_controls.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_status_view.dart';
|
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_status_view.dart';
|
||||||
|
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/gateway/view/gateway_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart';
|
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_sensor_batch_view.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_batch_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/view/wall_light_device_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_batch_controls.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_device_control.dart';
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/view/living_room_device_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_light_batch_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/view/wall_light_device_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_batch_control.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart';
|
import 'package:syncrow_web/pages/device_managment/wall_sensor/view/wall_sensor_conrtols.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_batch_control.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart';
|
||||||
|
|
||||||
mixin RouteControlsBasedCode {
|
mixin RouteControlsBasedCode {
|
||||||
Widget routeControlsWidgets({required AllDevicesModel device}) {
|
Widget routeControlsWidgets({required AllDevicesModel device}) {
|
||||||
switch (device.productType) {
|
switch (device.productType) {
|
||||||
|
case '1G':
|
||||||
|
return WallLightDeviceControl(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
);
|
||||||
|
case '2G':
|
||||||
|
return TwoGangDeviceControlView(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
);
|
||||||
case '3G':
|
case '3G':
|
||||||
return LivingRoomDeviceControl(
|
return LivingRoomDeviceControlsView(
|
||||||
deviceId: device.uuid!,
|
deviceId: device.uuid!,
|
||||||
);
|
);
|
||||||
case 'GW':
|
case 'GW':
|
||||||
return GateWayControls(
|
return GateWayControlsView(
|
||||||
gatewayId: device.uuid!,
|
gatewayId: device.uuid!,
|
||||||
);
|
);
|
||||||
case 'DL':
|
case 'DL':
|
||||||
return DoorLockView(device: device);
|
return DoorLockControlsView(device: device);
|
||||||
case 'WPS':
|
case 'WPS':
|
||||||
return WallSensorControls(device: device);
|
return WallSensorControlsView(device: device);
|
||||||
case 'CPS':
|
case 'CPS':
|
||||||
return CeilingSensorControls(
|
return CeilingSensorControlsView(
|
||||||
device: device,
|
device: device,
|
||||||
);
|
);
|
||||||
|
case 'CUR':
|
||||||
|
return CurtainStatusControlsView(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
);
|
||||||
case 'AC':
|
case 'AC':
|
||||||
return AcDeviceControl(device: device);
|
return AcDeviceControlsView(device: device);
|
||||||
|
case 'WH':
|
||||||
|
return WaterHeaterDeviceControlView(
|
||||||
|
device: device,
|
||||||
|
);
|
||||||
|
case 'DS':
|
||||||
|
return MainDoorSensorControlView(device: device);
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
3G:
|
||||||
|
1G:
|
||||||
|
2G:
|
||||||
|
GW:
|
||||||
|
DL:
|
||||||
|
WPS:
|
||||||
|
CPS:
|
||||||
|
AC:
|
||||||
|
CUR:
|
||||||
|
WH:
|
||||||
|
DS:
|
||||||
|
*/
|
||||||
|
|
||||||
|
Widget routeBatchControlsWidgets({required List<AllDevicesModel> devices}) {
|
||||||
|
switch (devices.first.productType) {
|
||||||
|
case '1G':
|
||||||
|
return WallLightBatchControlView(
|
||||||
|
deviceIds: devices
|
||||||
|
.where((e) => (e.productType == '1G'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case '2G':
|
||||||
|
return TwoGangBatchControlView(
|
||||||
|
deviceIds: devices
|
||||||
|
.where((e) => (e.productType == '2G'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case '3G':
|
||||||
|
return LivingRoomBatchControlsView(
|
||||||
|
deviceIds: devices
|
||||||
|
.where((e) => (e.productType == '3G'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'GW':
|
||||||
|
return GatewayBatchControlView(
|
||||||
|
gatewayIds: devices
|
||||||
|
.where((e) => (e.productType == 'GW'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'DL':
|
||||||
|
return DoorLockBatchControlView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'DL'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
|
case 'WPS':
|
||||||
|
return WallSensorBatchControlView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'WPS'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
|
case 'CPS':
|
||||||
|
return CeilingSensorBatchControlView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'CPS'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'CUR':
|
||||||
|
return CurtainBatchStatusView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'CUR'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'AC':
|
||||||
|
return AcDeviceBatchControlView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'AC'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList());
|
||||||
|
case 'WH':
|
||||||
|
return WaterHEaterBatchControlView(
|
||||||
|
deviceIds: devices
|
||||||
|
.where((e) => (e.productType == 'WH'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
case 'DS':
|
||||||
|
return MainDoorSensorBatchView(
|
||||||
|
devicesIds: devices
|
||||||
|
.where((e) => (e.productType == 'DS'))
|
||||||
|
.map((e) => e.uuid!)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
|
@ -13,11 +13,15 @@ class DeviceReport {
|
|||||||
|
|
||||||
DeviceReport.fromJson(Map<String, dynamic> json)
|
DeviceReport.fromJson(Map<String, dynamic> json)
|
||||||
: deviceUuid = json['deviceUuid'] as String?,
|
: deviceUuid = json['deviceUuid'] as String?,
|
||||||
startTime = json['startTime'] as int?,
|
startTime = int.tryParse(json['startTime'].toString()) ??
|
||||||
endTime = json['endTime'] as int?,
|
json['startTime'] as int?,
|
||||||
data = (json['data'] as List<dynamic>?)
|
endTime =
|
||||||
?.map((e) => DeviceEvent.fromJson(e as Map<String, dynamic>))
|
int.tryParse(json['endTime'].toString()) ?? json['endTime'] as int?,
|
||||||
.toList();
|
data = json['data'] != null
|
||||||
|
? (json['data'] as List<dynamic>?)
|
||||||
|
?.map((e) => DeviceEvent.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList()
|
||||||
|
: [];
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'deviceUuid': deviceUuid,
|
'deviceUuid': deviceUuid,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/room.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/unit.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
class AllDevicesModel {
|
class AllDevicesModel {
|
||||||
/*
|
/*
|
||||||
@ -62,6 +63,7 @@ class AllDevicesModel {
|
|||||||
int? updateTime;
|
int? updateTime;
|
||||||
String? uuid;
|
String? uuid;
|
||||||
int? batteryLevel;
|
int? batteryLevel;
|
||||||
|
String? productName;
|
||||||
|
|
||||||
AllDevicesModel({
|
AllDevicesModel({
|
||||||
this.room,
|
this.room,
|
||||||
@ -89,6 +91,7 @@ class AllDevicesModel {
|
|||||||
this.updateTime,
|
this.updateTime,
|
||||||
this.uuid,
|
this.uuid,
|
||||||
this.batteryLevel,
|
this.batteryLevel,
|
||||||
|
this.productName,
|
||||||
});
|
});
|
||||||
AllDevicesModel.fromJson(Map<String, dynamic> json) {
|
AllDevicesModel.fromJson(Map<String, dynamic> json) {
|
||||||
room = (json['room'] != null && (json['room'] is Map))
|
room = (json['room'] != null && (json['room'] is Map))
|
||||||
@ -105,7 +108,7 @@ class AllDevicesModel {
|
|||||||
categoryName = json['categoryName']?.toString();
|
categoryName = json['categoryName']?.toString();
|
||||||
createTime = int.tryParse(json['createTime']?.toString() ?? '');
|
createTime = int.tryParse(json['createTime']?.toString() ?? '');
|
||||||
gatewayId = json['gatewayId']?.toString();
|
gatewayId = json['gatewayId']?.toString();
|
||||||
icon = json['icon']?.toString();
|
icon = json['icon'] ?? _getDefaultIcon(productType);
|
||||||
ip = json['ip']?.toString();
|
ip = json['ip']?.toString();
|
||||||
lat = json['lat']?.toString();
|
lat = json['lat']?.toString();
|
||||||
localKey = json['localKey']?.toString();
|
localKey = json['localKey']?.toString();
|
||||||
@ -120,7 +123,37 @@ class AllDevicesModel {
|
|||||||
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
|
updateTime = int.tryParse(json['updateTime']?.toString() ?? '');
|
||||||
uuid = json['uuid']?.toString();
|
uuid = json['uuid']?.toString();
|
||||||
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
|
batteryLevel = int.tryParse(json['battery']?.toString() ?? '');
|
||||||
|
productName = json['productName']?.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _getDefaultIcon(String? productType) {
|
||||||
|
switch (productType) {
|
||||||
|
case 'LightBulb':
|
||||||
|
return Assets.lightBulb;
|
||||||
|
case 'CeilingSensor':
|
||||||
|
case 'WallSensor':
|
||||||
|
return Assets.sensors;
|
||||||
|
case 'AC':
|
||||||
|
return Assets.ac;
|
||||||
|
case 'DoorLock':
|
||||||
|
return Assets.doorLock;
|
||||||
|
case 'Curtain':
|
||||||
|
return Assets.curtain;
|
||||||
|
case '3G':
|
||||||
|
case '2G':
|
||||||
|
case '1G':
|
||||||
|
return Assets.gangSwitch;
|
||||||
|
case 'Gateway':
|
||||||
|
return Assets.gateway;
|
||||||
|
case 'WH':
|
||||||
|
return Assets.blackLogo;
|
||||||
|
case 'DS':
|
||||||
|
return Assets.sensors;
|
||||||
|
default:
|
||||||
|
return Assets.logo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final data = <String, dynamic>{};
|
final data = <String, dynamic>{};
|
||||||
if (room != null) {
|
if (room != null) {
|
||||||
@ -152,6 +185,70 @@ class AllDevicesModel {
|
|||||||
data['updateTime'] = updateTime;
|
data['updateTime'] = updateTime;
|
||||||
data['uuid'] = uuid;
|
data['uuid'] = uuid;
|
||||||
data['battery'] = batteryLevel;
|
data['battery'] = batteryLevel;
|
||||||
|
data['productName'] = productName;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is AllDevicesModel &&
|
||||||
|
other.room == room &&
|
||||||
|
other.unit == unit &&
|
||||||
|
other.productUuid == productUuid &&
|
||||||
|
other.productType == productType &&
|
||||||
|
other.permissionType == permissionType &&
|
||||||
|
other.activeTime == activeTime &&
|
||||||
|
other.category == category &&
|
||||||
|
other.categoryName == categoryName &&
|
||||||
|
other.createTime == createTime &&
|
||||||
|
other.gatewayId == gatewayId &&
|
||||||
|
other.icon == icon &&
|
||||||
|
other.ip == ip &&
|
||||||
|
other.lat == lat &&
|
||||||
|
other.localKey == localKey &&
|
||||||
|
other.lon == lon &&
|
||||||
|
other.model == model &&
|
||||||
|
other.name == name &&
|
||||||
|
other.nodeId == nodeId &&
|
||||||
|
other.online == online &&
|
||||||
|
other.ownerId == ownerId &&
|
||||||
|
other.sub == sub &&
|
||||||
|
other.timeZone == timeZone &&
|
||||||
|
other.updateTime == updateTime &&
|
||||||
|
other.uuid == uuid &&
|
||||||
|
other.productName == productName &&
|
||||||
|
other.batteryLevel == batteryLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return room.hashCode ^
|
||||||
|
unit.hashCode ^
|
||||||
|
productUuid.hashCode ^
|
||||||
|
productType.hashCode ^
|
||||||
|
permissionType.hashCode ^
|
||||||
|
activeTime.hashCode ^
|
||||||
|
category.hashCode ^
|
||||||
|
categoryName.hashCode ^
|
||||||
|
createTime.hashCode ^
|
||||||
|
gatewayId.hashCode ^
|
||||||
|
icon.hashCode ^
|
||||||
|
ip.hashCode ^
|
||||||
|
lat.hashCode ^
|
||||||
|
localKey.hashCode ^
|
||||||
|
lon.hashCode ^
|
||||||
|
model.hashCode ^
|
||||||
|
name.hashCode ^
|
||||||
|
nodeId.hashCode ^
|
||||||
|
online.hashCode ^
|
||||||
|
ownerId.hashCode ^
|
||||||
|
sub.hashCode ^
|
||||||
|
timeZone.hashCode ^
|
||||||
|
updateTime.hashCode ^
|
||||||
|
uuid.hashCode ^
|
||||||
|
productName.hashCode ^
|
||||||
|
batteryLevel.hashCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
class FactoryResetModel {
|
||||||
|
final List<String> devicesUuid;
|
||||||
|
|
||||||
|
FactoryResetModel({
|
||||||
|
required this.devicesUuid,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory FactoryResetModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return FactoryResetModel(
|
||||||
|
devicesUuid: List<String>.from(json['devicesUuid']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'devicesUuid': devicesUuid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
FactoryResetModel copyWith({
|
||||||
|
List<String>? devicesUuid,
|
||||||
|
}) {
|
||||||
|
return FactoryResetModel(
|
||||||
|
devicesUuid: devicesUuid ?? this.devicesUuid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
'devicesUuid': devicesUuid,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
factory FactoryResetModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return FactoryResetModel(
|
||||||
|
devicesUuid: List<String>.from(map['devicesUuid']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'FactoryReset(devicesUuid: $devicesUuid)';
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is FactoryResetModel &&
|
||||||
|
listEquals(other.devicesUuid, devicesUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => devicesUuid.hashCode;
|
||||||
|
}
|
@ -21,13 +21,11 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
rightBody: const NavigateHomeGridView(),
|
rightBody: const NavigateHomeGridView(),
|
||||||
enableMenuSideba: isLargeScreenSize(context),
|
|
||||||
scaffoldBody: BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
scaffoldBody: BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is DeviceManagementLoading) {
|
if (state is DeviceManagementLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is DeviceManagementLoaded ||
|
} else if (state is DeviceManagementLoaded || state is DeviceManagementFiltered) {
|
||||||
state is DeviceManagementFiltered) {
|
|
||||||
final devices = state is DeviceManagementLoaded
|
final devices = state is DeviceManagementLoaded
|
||||||
? state.devices
|
? state.devices
|
||||||
: (state as DeviceManagementFiltered).filteredDevices;
|
: (state as DeviceManagementFiltered).filteredDevices;
|
||||||
@ -42,3 +40,6 @@ class DeviceManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/custom_table.dart';
|
import 'package:syncrow_web/pages/common/custom_table.dart';
|
||||||
@ -27,6 +28,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
int offlineCount = 0;
|
int offlineCount = 0;
|
||||||
int lowBatteryCount = 0;
|
int lowBatteryCount = 0;
|
||||||
bool isControlButtonEnabled = false;
|
bool isControlButtonEnabled = false;
|
||||||
|
List<AllDevicesModel> selectedDevices = [];
|
||||||
|
|
||||||
if (state is DeviceManagementLoaded) {
|
if (state is DeviceManagementLoaded) {
|
||||||
devicesToShow = state.devices;
|
devicesToShow = state.devices;
|
||||||
@ -34,14 +36,18 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
onlineCount = state.onlineCount;
|
onlineCount = state.onlineCount;
|
||||||
offlineCount = state.offlineCount;
|
offlineCount = state.offlineCount;
|
||||||
lowBatteryCount = state.lowBatteryCount;
|
lowBatteryCount = state.lowBatteryCount;
|
||||||
isControlButtonEnabled = state.selectedDevice != null;
|
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||||
|
selectedDevices = state.selectedDevice ??
|
||||||
|
context.read<DeviceManagementBloc>().selectedDevices;
|
||||||
} else if (state is DeviceManagementFiltered) {
|
} else if (state is DeviceManagementFiltered) {
|
||||||
devicesToShow = state.filteredDevices;
|
devicesToShow = state.filteredDevices;
|
||||||
selectedIndex = state.selectedIndex;
|
selectedIndex = state.selectedIndex;
|
||||||
onlineCount = state.onlineCount;
|
onlineCount = state.onlineCount;
|
||||||
offlineCount = state.offlineCount;
|
offlineCount = state.offlineCount;
|
||||||
lowBatteryCount = state.lowBatteryCount;
|
lowBatteryCount = state.lowBatteryCount;
|
||||||
isControlButtonEnabled = state.selectedDevice != null;
|
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||||
|
selectedDevices = state.selectedDevice ??
|
||||||
|
context.read<DeviceManagementBloc>().selectedDevices;
|
||||||
} else if (state is DeviceManagementInitial) {
|
} else if (state is DeviceManagementInitial) {
|
||||||
devicesToShow = [];
|
devicesToShow = [];
|
||||||
selectedIndex = 0;
|
selectedIndex = 0;
|
||||||
@ -55,8 +61,11 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
'Low Battery ($lowBatteryCount)',
|
'Low Battery ($lowBatteryCount)',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
final buttonLabel =
|
||||||
|
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: isLargeScreenSize(context)
|
padding: isLargeScreenSize(context)
|
||||||
? const EdgeInsets.all(30)
|
? const EdgeInsets.all(30)
|
||||||
@ -69,7 +78,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
tabs: tabs,
|
tabs: tabs,
|
||||||
selectedIndex: selectedIndex,
|
selectedIndex: selectedIndex,
|
||||||
onTabChanged: (index) {
|
onTabChanged: (index) {
|
||||||
context.read<DeviceManagementBloc>()
|
context
|
||||||
|
.read<DeviceManagementBloc>()
|
||||||
.add(SelectedFilterChanged(index));
|
.add(SelectedFilterChanged(index));
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -78,25 +88,39 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
Container(
|
Container(
|
||||||
height: 45,
|
height: 45,
|
||||||
width: 100,
|
width: 125,
|
||||||
decoration: containerDecoration,
|
decoration: containerDecoration,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: isControlButtonEnabled
|
onPressed: isControlButtonEnabled
|
||||||
? () {
|
? () {
|
||||||
final selectedDevice = context
|
if (selectedDevices.length == 1) {
|
||||||
.read<DeviceManagementBloc>()
|
showDialog(
|
||||||
.selectedDevices.first;
|
context: context,
|
||||||
showDialog(
|
builder: (context) => DeviceControlDialog(
|
||||||
context: context,
|
device: selectedDevices.first,
|
||||||
builder: (context) => DeviceControlDialog(
|
),
|
||||||
device: selectedDevice),
|
);
|
||||||
);
|
} else if (selectedDevices.length > 1) {
|
||||||
|
final productTypes = selectedDevices
|
||||||
|
.map((device) => device.productType)
|
||||||
|
.toSet();
|
||||||
|
if (productTypes.length == 1) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
DeviceBatchControlDialog(
|
||||||
|
devices: selectedDevices,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
borderRadius: 9,
|
borderRadius: 9,
|
||||||
child: Text(
|
child: Text(
|
||||||
'Control',
|
buttonLabel,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: isControlButtonEnabled
|
color: isControlButtonEnabled
|
||||||
@ -116,7 +140,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
? const EdgeInsets.all(30)
|
? const EdgeInsets.all(30)
|
||||||
: const EdgeInsets.all(15),
|
: const EdgeInsets.all(15),
|
||||||
child: DynamicTable(
|
child: DynamicTable(
|
||||||
withSelectAll: false,
|
withSelectAll: true,
|
||||||
cellDecoration: containerDecoration,
|
cellDecoration: containerDecoration,
|
||||||
onRowSelected: (index, isSelected, row) {
|
onRowSelected: (index, isSelected, row) {
|
||||||
final selectedDevice = devicesToShow[index];
|
final selectedDevice = devicesToShow[index];
|
||||||
@ -140,8 +164,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
],
|
],
|
||||||
data: devicesToShow.map((device) {
|
data: devicesToShow.map((device) {
|
||||||
return [
|
return [
|
||||||
device.categoryName ?? '',
|
|
||||||
device.name ?? '',
|
device.name ?? '',
|
||||||
|
device.productName ?? '',
|
||||||
device.uuid ?? '',
|
device.uuid ?? '',
|
||||||
device.unit?.name ?? '',
|
device.unit?.name ?? '',
|
||||||
device.room?.name ?? '',
|
device.room?.name ?? '',
|
||||||
@ -155,6 +179,11 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
|||||||
(device.updateTime ?? 0) * 1000)),
|
(device.updateTime ?? 0) * 1000)),
|
||||||
];
|
];
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
onSelectionChanged: (selectedRows) {
|
||||||
|
context
|
||||||
|
.read<DeviceManagementBloc>()
|
||||||
|
.add(UpdateSelection(selectedRows));
|
||||||
|
},
|
||||||
initialSelectedIds: context
|
initialSelectedIds: context
|
||||||
.read<DeviceManagementBloc>()
|
.read<DeviceManagementBloc>()
|
||||||
.selectedDevices
|
.selectedDevices
|
||||||
|
@ -77,7 +77,9 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
|||||||
communityController.clear();
|
communityController.clear();
|
||||||
unitNameController.clear();
|
unitNameController.clear();
|
||||||
productNameController.clear();
|
productNameController.clear();
|
||||||
context.read<DeviceManagementBloc>().add(FetchDevices());
|
context.read<DeviceManagementBloc>()
|
||||||
|
..add(ResetFilters())
|
||||||
|
..add(FetchDevices());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,17 +14,21 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
|||||||
|
|
||||||
CeilingSensorBloc({required this.deviceId}) : super(CeilingInitialState()) {
|
CeilingSensorBloc({required this.deviceId}) : super(CeilingInitialState()) {
|
||||||
on<CeilingInitialEvent>(_fetchCeilingSensorStatus);
|
on<CeilingInitialEvent>(_fetchCeilingSensorStatus);
|
||||||
|
on<CeilingFetchDeviceStatusEvent>(_fetchCeilingSensorBatchControl);
|
||||||
on<CeilingChangeValueEvent>(_changeValue);
|
on<CeilingChangeValueEvent>(_changeValue);
|
||||||
|
on<CeilingBatchControlEvent>(_onBatchControl);
|
||||||
on<GetCeilingDeviceReportsEvent>(_getDeviceReports);
|
on<GetCeilingDeviceReportsEvent>(_getDeviceReports);
|
||||||
on<ShowCeilingDescriptionEvent>(_showDescription);
|
on<ShowCeilingDescriptionEvent>(_showDescription);
|
||||||
on<BackToCeilingGridViewEvent>(_backToGridView);
|
on<BackToCeilingGridViewEvent>(_backToGridView);
|
||||||
|
on<CeilingFactoryResetEvent>(_onFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchCeilingSensorStatus(
|
void _fetchCeilingSensorStatus(
|
||||||
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
|
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||||
emit(CeilingLoadingInitialState());
|
emit(CeilingLoadingInitialState());
|
||||||
try {
|
try {
|
||||||
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
var response =
|
||||||
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
||||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
// _listenToChanges();
|
// _listenToChanges();
|
||||||
@ -67,34 +71,76 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
|||||||
}
|
}
|
||||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
await _runDeBouncer(
|
await _runDeBouncer(
|
||||||
deviceId: deviceId, code: event.code, value: event.value, emit: emit);
|
deviceId: deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onBatchControl(
|
||||||
|
CeilingBatchControlEvent event, Emitter<CeilingSensorState> emit) async {
|
||||||
|
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||||
|
if (event.code == 'sensitivity') {
|
||||||
|
deviceStatus.sensitivity = event.value;
|
||||||
|
} else if (event.code == 'none_body_time') {
|
||||||
|
deviceStatus.noBodyTime = event.value;
|
||||||
|
} else if (event.code == 'moving_max_dis') {
|
||||||
|
deviceStatus.maxDistance = event.value;
|
||||||
|
} else if (event.code == 'scene') {
|
||||||
|
deviceStatus.spaceType = getSpaceType(event.value);
|
||||||
|
}
|
||||||
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
|
await _runDeBouncer(
|
||||||
|
deviceId: event.deviceIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_runDeBouncer({
|
_runDeBouncer({
|
||||||
required String deviceId,
|
required dynamic deviceId,
|
||||||
required String code,
|
required String code,
|
||||||
required dynamic value,
|
required dynamic value,
|
||||||
required Emitter<CeilingSensorState> emit,
|
required Emitter<CeilingSensorState> emit,
|
||||||
|
required bool isBatch,
|
||||||
}) {
|
}) {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
}
|
}
|
||||||
_timer = Timer(const Duration(seconds: 1), () async {
|
_timer = Timer(const Duration(seconds: 1), () async {
|
||||||
try {
|
try {
|
||||||
final response = await DevicesManagementApi()
|
late bool response;
|
||||||
.deviceControl(deviceId, Status(code: code, value: value));
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
add(CeilingInitialEvent());
|
add(CeilingInitialEvent(id));
|
||||||
}
|
}
|
||||||
if (response == true && code == 'scene') {
|
if (response == true && code == 'scene') {
|
||||||
emit(CeilingLoadingInitialState());
|
emit(CeilingLoadingInitialState());
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
add(CeilingInitialEvent());
|
add(CeilingInitialEvent(id));
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
add(CeilingInitialEvent());
|
add(CeilingInitialEvent(id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -128,4 +174,37 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
|||||||
BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
|
BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
|
||||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _fetchCeilingSensorBatchControl(
|
||||||
|
CeilingFetchDeviceStatusEvent event,
|
||||||
|
Emitter<CeilingSensorState> emit) async {
|
||||||
|
emit(CeilingLoadingInitialState());
|
||||||
|
try {
|
||||||
|
var response =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
||||||
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(CeilingFailedState(error: e.toString()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
CeilingFactoryResetEvent event, Emitter<CeilingSensorState> emit) async {
|
||||||
|
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryResetModel,
|
||||||
|
event.devicesId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const CeilingFailedState(error: 'Failed'));
|
||||||
|
} else {
|
||||||
|
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(CeilingFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
abstract class CeilingSensorEvent extends Equatable {
|
abstract class CeilingSensorEvent extends Equatable {
|
||||||
const CeilingSensorEvent();
|
const CeilingSensorEvent();
|
||||||
@ -7,7 +8,37 @@ abstract class CeilingSensorEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class CeilingInitialEvent extends CeilingSensorEvent {}
|
class CeilingInitialEvent extends CeilingSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
const CeilingInitialEvent(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CeilingFetchDeviceStatusEvent extends CeilingSensorEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
const CeilingFetchDeviceStatusEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CeilingBatchControlEvent extends CeilingSensorEvent {
|
||||||
|
final List<String> deviceIds;
|
||||||
|
final String code;
|
||||||
|
final dynamic value;
|
||||||
|
|
||||||
|
const CeilingBatchControlEvent({
|
||||||
|
required this.deviceIds,
|
||||||
|
required this.code,
|
||||||
|
required this.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
class CeilingChangeValueEvent extends CeilingSensorEvent {
|
class CeilingChangeValueEvent extends CeilingSensorEvent {
|
||||||
final dynamic value;
|
final dynamic value;
|
||||||
@ -39,3 +70,16 @@ class ShowCeilingDescriptionEvent extends CeilingSensorEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BackToCeilingGridViewEvent extends CeilingSensorEvent {}
|
class BackToCeilingGridViewEvent extends CeilingSensorEvent {}
|
||||||
|
|
||||||
|
class CeilingFactoryResetEvent extends CeilingSensorEvent {
|
||||||
|
final String devicesId;
|
||||||
|
final FactoryResetModel factoryResetModel;
|
||||||
|
|
||||||
|
const CeilingFactoryResetEvent({
|
||||||
|
required this.devicesId,
|
||||||
|
required this.factoryResetModel,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesId, factoryResetModel];
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
class CeilingSensorModel {
|
class CeilingSensorModel {
|
||||||
@ -91,6 +92,30 @@ class CeilingSensorModel {
|
|||||||
spaceType: _spaceType,
|
spaceType: _spaceType,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CeilingSensorModel copyWith({
|
||||||
|
String? presenceState,
|
||||||
|
int? sensitivity,
|
||||||
|
String? checkingResult,
|
||||||
|
int? presenceRange,
|
||||||
|
int? sportsPara,
|
||||||
|
String? bodyMovement,
|
||||||
|
String? noBodyTime,
|
||||||
|
int? maxDistance,
|
||||||
|
SpaceTypes? spaceType,
|
||||||
|
}) {
|
||||||
|
return CeilingSensorModel(
|
||||||
|
presenceState: presenceState ?? this.presenceState,
|
||||||
|
sensitivity: sensitivity ?? this.sensitivity,
|
||||||
|
checkingResult: checkingResult ?? this.checkingResult,
|
||||||
|
presenceRange: presenceRange ?? this.presenceRange,
|
||||||
|
sportsPara: sportsPara ?? this.sportsPara,
|
||||||
|
bodyMovement: bodyMovement ?? this.bodyMovement,
|
||||||
|
noBodyTime: noBodyTime ?? this.noBodyTime,
|
||||||
|
maxDistance: maxDistance ?? this.maxDistance,
|
||||||
|
spaceType: spaceType ?? this.spaceType,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SpaceTypes {
|
enum SpaceTypes {
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
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/ceiling_sensor/bloc/bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.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';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_update_data.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presense_nobody_time.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class CeilingSensorBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const CeilingSensorBatchControlView({super.key, required this.devicesIds});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CeilingSensorBloc(deviceId: devicesIds.first)
|
||||||
|
..add(CeilingFetchDeviceStatusEvent(devicesIds)),
|
||||||
|
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
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 const Center(child: Text('Error fetching status'));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||||
|
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||||
|
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: [
|
||||||
|
PresenceSpaceType(
|
||||||
|
description: 'Space Type',
|
||||||
|
value: model.spaceType,
|
||||||
|
action: (String value) => context.read<CeilingSensorBloc>().add(
|
||||||
|
CeilingBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: 'scene',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.sensitivity.toDouble(),
|
||||||
|
title: 'Sensitivity:',
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 10,
|
||||||
|
steps: 1,
|
||||||
|
action: (int value) {
|
||||||
|
context.read<CeilingSensorBloc>().add(
|
||||||
|
CeilingBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: 'sensitivity',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
PresenceUpdateData(
|
||||||
|
value: model.maxDistance.toDouble(),
|
||||||
|
title: 'Maximum Distance:',
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: 500,
|
||||||
|
steps: 50,
|
||||||
|
description: 'm',
|
||||||
|
action: (int value) => context.read<CeilingSensorBloc>().add(
|
||||||
|
CeilingBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: 'moving_max_dis',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PresenceNoBodyTime(
|
||||||
|
value: model.noBodyTime,
|
||||||
|
title: 'Nobody Time:',
|
||||||
|
description: '',
|
||||||
|
action: (String value) => context.read<CeilingSensorBloc>().add(
|
||||||
|
CeilingBatchControlEvent(
|
||||||
|
deviceIds: devicesIds,
|
||||||
|
code: 'nobody_time',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 4),
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<CeilingSensorBloc>().add(
|
||||||
|
CeilingFactoryResetEvent(
|
||||||
|
devicesId: devicesIds.first,
|
||||||
|
factoryResetModel:
|
||||||
|
FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -16,9 +16,9 @@ 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/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class CeilingSensorControls extends StatelessWidget
|
class CeilingSensorControlsView extends StatelessWidget
|
||||||
with HelperResponsiveLayout {
|
with HelperResponsiveLayout {
|
||||||
const CeilingSensorControls({super.key, required this.device});
|
const CeilingSensorControlsView({super.key, required this.device});
|
||||||
|
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ class CeilingSensorControls extends StatelessWidget
|
|||||||
final isMedium = isMediumScreenSize(context);
|
final isMedium = isMediumScreenSize(context);
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) => CeilingSensorBloc(deviceId: device.uuid ?? '')
|
create: (context) => CeilingSensorBloc(deviceId: device.uuid ?? '')
|
||||||
..add(CeilingInitialEvent()),
|
..add(CeilingInitialEvent(device.uuid ?? '')),
|
||||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is CeilingLoadingInitialState ||
|
if (state is CeilingLoadingInitialState ||
|
||||||
@ -112,7 +112,7 @@ class CeilingSensorControls extends StatelessWidget
|
|||||||
value: model.sensitivity.toDouble(),
|
value: model.sensitivity.toDouble(),
|
||||||
title: 'Sensitivity:',
|
title: 'Sensitivity:',
|
||||||
minValue: 1,
|
minValue: 1,
|
||||||
maxValue: 5,
|
maxValue: 10,
|
||||||
steps: 1,
|
steps: 1,
|
||||||
action: (int value) {
|
action: (int value) {
|
||||||
context.read<CeilingSensorBloc>().add(
|
context.read<CeilingSensorBloc>().add(
|
||||||
|
161
lib/pages/device_managment/curtain/bloc/curtain_bloc.dart
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
class CurtainBloc extends Bloc<CurtainEvent, CurtainState> {
|
||||||
|
late bool deviceStatus;
|
||||||
|
final String deviceId;
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
CurtainBloc({required this.deviceId}) : super(CurtainInitial()) {
|
||||||
|
on<CurtainFetchDeviceStatus>(_onFetchDeviceStatus);
|
||||||
|
on<CurtainFetchBatchStatus>(_onFetchBatchStatus);
|
||||||
|
on<CurtainControl>(_onCurtainControl);
|
||||||
|
on<CurtainBatchControl>(_onCurtainBatchControl);
|
||||||
|
on<CurtainFactoryReset>(_onFactoryReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchDeviceStatus(
|
||||||
|
CurtainFetchDeviceStatus event, Emitter<CurtainState> emit) async {
|
||||||
|
emit(CurtainStatusLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
|
|
||||||
|
deviceStatus = _checkStatus(status.status[0].value);
|
||||||
|
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onCurtainControl(
|
||||||
|
CurtainControl event, Emitter<CurtainState> emit) async {
|
||||||
|
final oldValue = deviceStatus;
|
||||||
|
|
||||||
|
_updateLocalValue(event.value, emit);
|
||||||
|
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _runDebounce({
|
||||||
|
required dynamic deviceId,
|
||||||
|
required String code,
|
||||||
|
required bool value,
|
||||||
|
required bool oldValue,
|
||||||
|
required Emitter<CurtainState> emit,
|
||||||
|
required bool isBatch,
|
||||||
|
}) async {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer!.cancel();
|
||||||
|
}
|
||||||
|
_timer = Timer(const Duration(seconds: 1), () async {
|
||||||
|
try {
|
||||||
|
final controlValue = value ? 'open' : 'close';
|
||||||
|
|
||||||
|
late bool response;
|
||||||
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, controlValue);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: controlValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
_revertValueAndEmit(id, oldValue, emit);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_revertValueAndEmit(id, oldValue, emit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _revertValueAndEmit(
|
||||||
|
String deviceId, bool oldValue, Emitter<CurtainState> emit) {
|
||||||
|
_updateLocalValue(oldValue, emit);
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
emit(const CurtainControlError('Failed to control the device.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateLocalValue(bool value, Emitter<CurtainState> emit) {
|
||||||
|
deviceStatus = value;
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _checkStatus(String command) {
|
||||||
|
return command.toLowerCase() == 'open';
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchBatchStatus(
|
||||||
|
CurtainFetchBatchStatus event, Emitter<CurtainState> emit) async {
|
||||||
|
emit(CurtainStatusLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
|
||||||
|
deviceStatus = _checkStatus(status.status[0].value);
|
||||||
|
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onCurtainBatchControl(
|
||||||
|
CurtainBatchControl event, Emitter<CurtainState> emit) async {
|
||||||
|
final oldValue = deviceStatus;
|
||||||
|
|
||||||
|
_updateLocalValue(event.value, emit);
|
||||||
|
|
||||||
|
emit(CurtainStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.devicesIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
CurtainFactoryReset event, Emitter<CurtainState> emit) async {
|
||||||
|
emit(CurtainStatusLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const CurtainControlError('Failed'));
|
||||||
|
} else {
|
||||||
|
add(CurtainFetchDeviceStatus(event.deviceId));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(CurtainControlError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
lib/pages/device_managment/curtain/bloc/curtain_event.dart
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
|
sealed class CurtainEvent extends Equatable {
|
||||||
|
const CurtainEvent();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainFetchDeviceStatus extends CurtainEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CurtainFetchDeviceStatus(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainFetchBatchStatus extends CurtainEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
const CurtainFetchBatchStatus(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainControl extends CurtainEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
const CurtainControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainBatchControl extends CurtainEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
const CurtainBatchControl(
|
||||||
|
{required this.devicesIds, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainFactoryReset extends CurtainEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
const CurtainFactoryReset(
|
||||||
|
{required this.deviceId, required this.factoryReset});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
40
lib/pages/device_managment/curtain/bloc/curtain_state.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
sealed class CurtainState extends Equatable {
|
||||||
|
const CurtainState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class CurtainInitial extends CurtainState {}
|
||||||
|
|
||||||
|
class CurtainStatusLoading extends CurtainState {}
|
||||||
|
|
||||||
|
class CurtainStatusLoaded extends CurtainState {
|
||||||
|
final bool status;
|
||||||
|
|
||||||
|
const CurtainStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainError extends CurtainState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const CurtainError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class CurtainControlError extends CurtainState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
const CurtainControlError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
32
lib/pages/device_managment/curtain/model/curtain_model.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
|
class CurtainModel {
|
||||||
|
final String productUuid;
|
||||||
|
final String productType;
|
||||||
|
final List<Status> status;
|
||||||
|
|
||||||
|
CurtainModel({
|
||||||
|
required this.productUuid,
|
||||||
|
required this.productType,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory CurtainModel.fromJson(dynamic json) {
|
||||||
|
var statusList = json['status'] as List;
|
||||||
|
List<Status> status = statusList.map((i) => Status.fromJson(i)).toList();
|
||||||
|
|
||||||
|
return CurtainModel(
|
||||||
|
productUuid: json['productUuid'],
|
||||||
|
productType: json['productType'],
|
||||||
|
status: status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'productUuid': productUuid,
|
||||||
|
'productType': productType,
|
||||||
|
'status': status.map((s) => s.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/curtain_toggle.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.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';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class CurtainBatchStatusView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const CurtainBatchStatusView({super.key, required this.devicesIds});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainBloc(deviceId: devicesIds.first)
|
||||||
|
..add(CurtainFetchBatchStatus(devicesIds)),
|
||||||
|
child: BlocBuilder<CurtainBloc, CurtainState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainStatusLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is CurtainStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is CurtainError || state is CurtainControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context, bool status) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return GridView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: isLarge || isExtraLarge
|
||||||
|
? 3
|
||||||
|
: isMedium
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
|
mainAxisExtent: 140,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
mainAxisSpacing: 12,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
CurtainToggle(
|
||||||
|
value: status,
|
||||||
|
code: 'control',
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
label: 'Curtains',
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<CurtainBloc>().add(CurtainBatchControl(
|
||||||
|
devicesIds: devicesIds,
|
||||||
|
code: 'control',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(deviceId: devicesIds.first, version: 5),
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<CurtainBloc>().add(
|
||||||
|
CurtainFactoryReset(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/curtain_toggle.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/curtain/bloc/curtain_state.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class CurtainStatusControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const CurtainStatusControlsView({super.key, required this.deviceId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => CurtainBloc(deviceId: deviceId)
|
||||||
|
..add(CurtainFetchDeviceStatus(deviceId)),
|
||||||
|
child: BlocBuilder<CurtainBloc, CurtainState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CurtainStatusLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is CurtainStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is CurtainError || state is CurtainControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context, bool status) {
|
||||||
|
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: [
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
CurtainToggle(
|
||||||
|
value: status,
|
||||||
|
code: 'control',
|
||||||
|
deviceId: deviceId,
|
||||||
|
label: 'Curtains',
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<CurtainBloc>().add(
|
||||||
|
CurtainControl(
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'control',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
|
|||||||
on<DoorLockFetchStatus>(_onFetchDeviceStatus);
|
on<DoorLockFetchStatus>(_onFetchDeviceStatus);
|
||||||
//on<DoorLockControl>(_onDoorLockControl);
|
//on<DoorLockControl>(_onDoorLockControl);
|
||||||
on<UpdateLockEvent>(_updateLock);
|
on<UpdateLockEvent>(_updateLock);
|
||||||
|
on<DoorLockFactoryReset>(_onFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onFetchDeviceStatus(
|
FutureOr<void> _onFetchDeviceStatus(
|
||||||
@ -113,4 +114,22 @@ class DoorLockBloc extends Bloc<DoorLockEvent, DoorLockState> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
DoorLockFactoryReset event, Emitter<DoorLockState> emit) async {
|
||||||
|
emit(DoorLockStatusLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const DoorLockControlError('Failed'));
|
||||||
|
} else {
|
||||||
|
add(DoorLockFetchStatus(event.deviceId));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(DoorLockControlError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
sealed class DoorLockEvent extends Equatable {
|
sealed class DoorLockEvent extends Equatable {
|
||||||
const DoorLockEvent();
|
const DoorLockEvent();
|
||||||
@ -37,3 +38,16 @@ class UpdateLockEvent extends DoorLockEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [value];
|
List<Object> get props => [value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DoorLockFactoryReset extends DoorLockEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
const DoorLockFactoryReset({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.factoryReset,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
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/door_lock/bloc/door_lock_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_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';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class DoorLockBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const DoorLockBatchControlView({super.key, required this.devicesIds});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@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<DoorLockBloc>(context).add(
|
||||||
|
DoorLockFactoryReset(
|
||||||
|
deviceId: devicesIds.first,
|
||||||
|
factoryReset: FactoryResetModel(devicesUuid: devicesIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,10 +7,10 @@ import 'package:syncrow_web/pages/device_managment/door_lock/bloc/door_lock_stat
|
|||||||
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/models/door_lock_status_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/door_lock/widget/door_button.dart';
|
import 'package:syncrow_web/pages/device_managment/door_lock/widget/door_button.dart';
|
||||||
|
|
||||||
class DoorLockView extends StatelessWidget {
|
class DoorLockControlsView extends StatelessWidget {
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
|
|
||||||
const DoorLockView({super.key, required this.device});
|
const DoorLockControlsView({super.key, required this.device});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
||||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
|||||||
GateWayBloc() : super(GateWayInitial()) {
|
GateWayBloc() : super(GateWayInitial()) {
|
||||||
on<GateWayFetch>((event, emit) {});
|
on<GateWayFetch>((event, emit) {});
|
||||||
on<GatWayById>(_getGatWayById);
|
on<GatWayById>(_getGatWayById);
|
||||||
|
on<GateWayFactoryReset>(_onFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _getGatWayById(
|
FutureOr<void> _getGatWayById(
|
||||||
@ -27,4 +29,22 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
GateWayFactoryReset event, Emitter<GateWayState> emit) async {
|
||||||
|
emit(GatewayLoadingState());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const ErrorState(message: 'Failed'));
|
||||||
|
} else {
|
||||||
|
add(GatWayById(event.deviceId));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(ErrorState(message: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,3 +18,15 @@ class GatWayById extends GateWayEvent {
|
|||||||
final String getWayId;
|
final String getWayId;
|
||||||
const GatWayById(this.getWayId);
|
const GatWayById(this.getWayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GateWayFactoryReset extends GateWayEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
const GateWayFactoryReset({
|
||||||
|
required this.deviceId,
|
||||||
|
required this.factoryReset,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
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/gateway/bloc/gate_way_bloc.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';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class GatewayBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const GatewayBatchControlView({super.key, required this.gatewayIds});
|
||||||
|
|
||||||
|
final List<String> gatewayIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => GateWayBloc()..add(GatWayById(gatewayIds.first)),
|
||||||
|
child: BlocBuilder<GateWayBloc, GateWayState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is GatewayLoadingState) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is UpdateGatewayState) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 170,
|
||||||
|
height: 140,
|
||||||
|
child: FirmwareUpdateWidget(
|
||||||
|
deviceId: gatewayIds.first, version: 2)),
|
||||||
|
const SizedBox(
|
||||||
|
width: 12,
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 170,
|
||||||
|
height: 140,
|
||||||
|
child: FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<GateWayBloc>().add(
|
||||||
|
GateWayFactoryReset(
|
||||||
|
deviceId: gatewayIds.first,
|
||||||
|
factoryReset:
|
||||||
|
FactoryResetModel(devicesUuid: gatewayIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -7,8 +7,8 @@ import 'package:syncrow_web/pages/visitor_password/model/device_model.dart';
|
|||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class GateWayControls extends StatelessWidget with HelperResponsiveLayout {
|
class GateWayControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||||
const GateWayControls({super.key, required this.gatewayId});
|
const GateWayControlsView({super.key, required this.gatewayId});
|
||||||
|
|
||||||
final String gatewayId;
|
final String gatewayId;
|
||||||
|
|
||||||
@ -67,26 +67,23 @@ class _DeviceItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
ClipOval(
|
||||||
width: 60,
|
child: Container(
|
||||||
height: 60,
|
height: 60,
|
||||||
decoration: const BoxDecoration(
|
width: 60,
|
||||||
shape: BoxShape.circle,
|
padding: const EdgeInsets.all(8),
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
device.icon,
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
)),
|
||||||
padding: const EdgeInsets.all(4),
|
|
||||||
child: ClipOval(
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
device.icon,
|
|
||||||
fit: BoxFit.fill,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Text(
|
Text(
|
||||||
device.name ?? 'Unknown Device',
|
device.name ?? 'Unknown Device',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.start,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
@ -0,0 +1,159 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/bloc/main_door_sensor_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
class MainDoorSensorBloc
|
||||||
|
extends Bloc<MainDoorSensorEvent, MainDoorSensorState> {
|
||||||
|
MainDoorSensorBloc() : super(MainDoorSensorInitial()) {
|
||||||
|
on<MainDoorSensorFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||||
|
on<MainDoorSensorControl>(_onControl);
|
||||||
|
on<MainDoorSensorFetchBatchEvent>(_onFetchBatchStatus);
|
||||||
|
on<MainDoorSensorReportsEvent>(_fetchReports);
|
||||||
|
on<MainDoorSensorFactoryReset>(_factoryReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
late MainDoorSensorStatusModel deviceStatus;
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchDeviceStatus(MainDoorSensorFetchDeviceEvent event,
|
||||||
|
Emitter<MainDoorSensorState> emit) async {
|
||||||
|
emit(MainDoorSensorLoadingState());
|
||||||
|
try {
|
||||||
|
final status = await DevicesManagementApi()
|
||||||
|
.getDeviceStatus(event.deviceId)
|
||||||
|
.then((value) => value.status);
|
||||||
|
|
||||||
|
deviceStatus = MainDoorSensorStatusModel.fromJson(event.deviceId, status);
|
||||||
|
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(MainDoorSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onControl(
|
||||||
|
MainDoorSensorControl event, Emitter<MainDoorSensorState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _runDebounce({
|
||||||
|
required String deviceId,
|
||||||
|
required String code,
|
||||||
|
required bool value,
|
||||||
|
required bool oldValue,
|
||||||
|
required Emitter<MainDoorSensorState> emit,
|
||||||
|
}) async {
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer!.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (e is DioException && e.response != null) {
|
||||||
|
debugPrint('Error response: ${e.response?.data}');
|
||||||
|
}
|
||||||
|
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
|
||||||
|
Emitter<MainDoorSensorState> emit) {
|
||||||
|
_updateLocalValue(code, oldValue);
|
||||||
|
emit(MainDoorSensorDeviceStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateLocalValue(String code, bool value) {
|
||||||
|
switch (code) {
|
||||||
|
case 'doorcontact_state':
|
||||||
|
deviceStatus = deviceStatus.copyWith(doorContactState: value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _getValueByCode(String code) {
|
||||||
|
switch (code) {
|
||||||
|
case 'doorcontact_state':
|
||||||
|
return deviceStatus.doorContactState;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch batch status for multiple devices (if needed)
|
||||||
|
FutureOr<void> _onFetchBatchStatus(MainDoorSensorFetchBatchEvent event,
|
||||||
|
Emitter<MainDoorSensorState> emit) async {
|
||||||
|
emit(MainDoorSensorLoadingState());
|
||||||
|
try {
|
||||||
|
// final batchStatus =
|
||||||
|
// await DevicesManagementApi().getBatchDeviceStatus(event.deviceIds);
|
||||||
|
// Assuming you need to update multiple devices status here
|
||||||
|
// You might need a list or map of MainDoorSensorStatusModel for batch processing
|
||||||
|
// emit(MainDoorSensorBatchStatusLoaded(batchStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(MainDoorSensorBatchFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _fetchReports(MainDoorSensorReportsEvent event,
|
||||||
|
Emitter<MainDoorSensorState> emit) async {
|
||||||
|
emit(MainDoorSensorLoadingState());
|
||||||
|
try {
|
||||||
|
final reports = await DevicesManagementApi.getDeviceReportsByDate(
|
||||||
|
event.deviceId, event.code, event.from, event.to);
|
||||||
|
emit(MainDoorSensorReportLoaded(reports));
|
||||||
|
} catch (e) {
|
||||||
|
emit(MainDoorSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_timer?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _factoryReset(MainDoorSensorFactoryReset event,
|
||||||
|
Emitter<MainDoorSensorState> emit) async {
|
||||||
|
emit(MainDoorSensorLoadingState());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(MainDoorSensorFailedState(error: 'Failed'));
|
||||||
|
} else {
|
||||||
|
add(MainDoorSensorFetchDeviceEvent(event.deviceId));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(MainDoorSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
import '../../all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
|
class MainDoorSensorEvent extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorFetchDeviceEvent extends MainDoorSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
MainDoorSensorFetchDeviceEvent(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorFetchBatchEvent extends MainDoorSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
MainDoorSensorFetchBatchEvent(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorControl extends MainDoorSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
MainDoorSensorControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorBatchControl extends MainDoorSensorEvent {
|
||||||
|
final List<String> deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
MainDoorSensorBatchControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorReportsEvent extends MainDoorSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String code;
|
||||||
|
final String from;
|
||||||
|
final String to;
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, from, to];
|
||||||
|
|
||||||
|
MainDoorSensorReportsEvent(
|
||||||
|
{required this.deviceId,
|
||||||
|
required this.code,
|
||||||
|
required this.from,
|
||||||
|
required this.to});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorFactoryReset extends MainDoorSensorEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
MainDoorSensorFactoryReset(
|
||||||
|
{required this.deviceId, required this.factoryReset});
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart';
|
||||||
|
|
||||||
|
class MainDoorSensorState extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorInitial extends MainDoorSensorState {}
|
||||||
|
|
||||||
|
class MainDoorSensorLoadingState extends MainDoorSensorState {}
|
||||||
|
|
||||||
|
class MainDoorSensorDeviceStatusLoaded extends MainDoorSensorState {
|
||||||
|
final MainDoorSensorStatusModel status;
|
||||||
|
|
||||||
|
MainDoorSensorDeviceStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorFailedState extends MainDoorSensorState {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
MainDoorSensorFailedState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [error];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorBatchFailedState extends MainDoorSensorState {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
MainDoorSensorBatchFailedState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [error];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorBatchStatusLoaded extends MainDoorSensorState {
|
||||||
|
final List<MainDoorSensorStatusModel> status;
|
||||||
|
|
||||||
|
MainDoorSensorBatchStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorReportLoaded extends MainDoorSensorState {
|
||||||
|
final DeviceReport deviceReport;
|
||||||
|
|
||||||
|
MainDoorSensorReportLoaded(this.deviceReport);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [deviceReport];
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainDoorSensorReportsLoadingState extends MainDoorSensorState {}
|
||||||
|
|
||||||
|
class MainDoorSensorReportsFailedState extends MainDoorSensorState {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
MainDoorSensorReportsFailedState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [error];
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
|
class MainDoorSensorStatusModel {
|
||||||
|
final String uuid;
|
||||||
|
final bool doorContactState;
|
||||||
|
final int batteryPercentage;
|
||||||
|
|
||||||
|
MainDoorSensorStatusModel({
|
||||||
|
required this.uuid,
|
||||||
|
required this.doorContactState,
|
||||||
|
required this.batteryPercentage,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MainDoorSensorStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||||
|
late bool doorContactState = false;
|
||||||
|
late int batteryPercentage = 0;
|
||||||
|
|
||||||
|
for (var status in jsonList) {
|
||||||
|
switch (status.code) {
|
||||||
|
case 'doorcontact_state':
|
||||||
|
doorContactState = status.value ?? false;
|
||||||
|
break;
|
||||||
|
case 'battery_percentage':
|
||||||
|
batteryPercentage = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MainDoorSensorStatusModel(
|
||||||
|
uuid: id,
|
||||||
|
doorContactState: doorContactState,
|
||||||
|
batteryPercentage: batteryPercentage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MainDoorSensorStatusModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
bool? doorContactState,
|
||||||
|
int? batteryPercentage,
|
||||||
|
}) {
|
||||||
|
return MainDoorSensorStatusModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
doorContactState: doorContactState ?? this.doorContactState,
|
||||||
|
batteryPercentage: batteryPercentage ?? this.batteryPercentage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,197 @@
|
|||||||
|
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/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/main_door_sensor/bloc/main_door_sensor_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/models/main_door_status_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/main_door_sensor/widgets/notification_dialog.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.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 MainDoorSensorControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const MainDoorSensorControlView({super.key, required this.device});
|
||||||
|
|
||||||
|
final AllDevicesModel device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => MainDoorSensorBloc()
|
||||||
|
..add(MainDoorSensorFetchDeviceEvent(device.uuid!)),
|
||||||
|
child: BlocBuilder<MainDoorSensorBloc, MainDoorSensorState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is MainDoorSensorLoadingState ||
|
||||||
|
state is MainDoorSensorReportsLoadingState) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is MainDoorSensorDeviceStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is MainDoorSensorReportLoaded) {
|
||||||
|
return ReportsTable(
|
||||||
|
report: state.deviceReport,
|
||||||
|
onRowTap: (index) {},
|
||||||
|
onClose: () {
|
||||||
|
context
|
||||||
|
.read<MainDoorSensorBloc>()
|
||||||
|
.add(MainDoorSensorFetchDeviceEvent(device.uuid!));
|
||||||
|
},
|
||||||
|
hideValueShowDescription: true,
|
||||||
|
mainDoorSensor: true,
|
||||||
|
);
|
||||||
|
} else if (state is MainDoorSensorFailedState ||
|
||||||
|
state is MainDoorSensorBatchFailedState) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, MainDoorSensorStatusModel status) {
|
||||||
|
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: [
|
||||||
|
IconNameStatusContainer(
|
||||||
|
isFullIcon: true,
|
||||||
|
name: status.doorContactState ? 'Open' : 'Close',
|
||||||
|
icon: Assets.openCloseDoor,
|
||||||
|
onTap: () {},
|
||||||
|
status: status.doorContactState,
|
||||||
|
textColor: status.doorContactState
|
||||||
|
? ColorsManager.red
|
||||||
|
: ColorsManager.blackColor,
|
||||||
|
paddingAmount: 8,
|
||||||
|
),
|
||||||
|
IconNameStatusContainer(
|
||||||
|
isFullIcon: true,
|
||||||
|
name: 'Open/Close\nRecord',
|
||||||
|
icon: Assets.openCloseRecords,
|
||||||
|
onTap: () {
|
||||||
|
final from = DateTime.now()
|
||||||
|
.subtract(const Duration(days: 30))
|
||||||
|
.millisecondsSinceEpoch;
|
||||||
|
final to = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
context.read<MainDoorSensorBloc>().add(
|
||||||
|
MainDoorSensorReportsEvent(
|
||||||
|
deviceId: device.uuid!,
|
||||||
|
code: 'doorcontact_state',
|
||||||
|
from: from.toString(),
|
||||||
|
to: to.toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
IconNameStatusContainer(
|
||||||
|
isFullIcon: false,
|
||||||
|
name: 'Notifications\nSettings',
|
||||||
|
icon: Assets.mainDoorNotifi,
|
||||||
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const NotificationDialog(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
status: false,
|
||||||
|
textColor: ColorsManager.blackColor,
|
||||||
|
paddingAmount: 14,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IconNameStatusContainer extends StatelessWidget {
|
||||||
|
const IconNameStatusContainer({
|
||||||
|
super.key,
|
||||||
|
required this.name,
|
||||||
|
required this.icon,
|
||||||
|
required this.onTap,
|
||||||
|
required this.status,
|
||||||
|
required this.textColor,
|
||||||
|
this.paddingAmount = 12,
|
||||||
|
required this.isFullIcon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
final String icon;
|
||||||
|
final GestureTapCallback onTap;
|
||||||
|
final bool status;
|
||||||
|
final Color textColor;
|
||||||
|
final double? paddingAmount;
|
||||||
|
final bool isFullIcon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: DeviceControlsContainer(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
if (isFullIcon)
|
||||||
|
ClipOval(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Container(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
),
|
||||||
|
//margin: const EdgeInsets.symmetric(horizontal: 4),
|
||||||
|
padding: EdgeInsets.all(paddingAmount ?? 12),
|
||||||
|
child: ClipOval(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
|
child: Text(
|
||||||
|
name,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
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/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});
|
||||||
|
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
@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),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class NotificationDialog extends StatelessWidget {
|
||||||
|
const NotificationDialog({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
insetPadding: const EdgeInsets.all(20),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: 798,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const SizedBox(),
|
||||||
|
Text(
|
||||||
|
'Notification Settings',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 22,
|
||||||
|
color: ColorsManager.dialogBlueTitle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 25,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.grey,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
padding: EdgeInsets.all(1),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ToggleWidget(
|
||||||
|
value: true,
|
||||||
|
code: 'notification',
|
||||||
|
deviceId: '',
|
||||||
|
label: 'Low Battery',
|
||||||
|
onChange: (v) {},
|
||||||
|
icon: '-1',
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
value: true,
|
||||||
|
code: 'notification',
|
||||||
|
deviceId: '',
|
||||||
|
label: 'Closing\nReminders',
|
||||||
|
onChange: (v) {},
|
||||||
|
icon: '-1',
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
value: true,
|
||||||
|
code: 'notification',
|
||||||
|
deviceId: '',
|
||||||
|
label: 'Door Alarm',
|
||||||
|
onChange: (v) {},
|
||||||
|
icon: '-1',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,175 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
class WallLightSwitchBloc
|
||||||
|
extends Bloc<WallLightSwitchEvent, WallLightSwitchState> {
|
||||||
|
WallLightSwitchBloc({required this.deviceId})
|
||||||
|
: super(WallLightSwitchInitial()) {
|
||||||
|
on<WallLightSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||||
|
on<WallLightSwitchControl>(_onControl);
|
||||||
|
on<WallLightSwitchFetchBatchEvent>(_onFetchBatchStatus);
|
||||||
|
on<WallLightSwitchBatchControl>(_onBatchControl);
|
||||||
|
on<WallLightFactoryReset>(_onFactoryReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
late WallLightStatusModel deviceStatus;
|
||||||
|
final String deviceId;
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchDeviceStatus(WallLightSwitchFetchDeviceEvent event,
|
||||||
|
Emitter<WallLightSwitchState> emit) async {
|
||||||
|
emit(WallLightSwitchLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
|
|
||||||
|
deviceStatus =
|
||||||
|
WallLightStatusModel.fromJson(event.deviceId, status.status);
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(WallLightSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onControl(
|
||||||
|
WallLightSwitchControl event, Emitter<WallLightSwitchState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _runDebounce({
|
||||||
|
required dynamic deviceId,
|
||||||
|
required String code,
|
||||||
|
required bool value,
|
||||||
|
required bool oldValue,
|
||||||
|
required Emitter<WallLightSwitchState> emit,
|
||||||
|
required bool isBatch,
|
||||||
|
}) async {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer!.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||||
|
try {
|
||||||
|
late bool response;
|
||||||
|
|
||||||
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
|
||||||
|
Emitter<WallLightSwitchState> emit) {
|
||||||
|
_updateLocalValue(code, oldValue);
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateLocalValue(String code, bool value) {
|
||||||
|
if (code == 'switch_1') {
|
||||||
|
deviceStatus = deviceStatus.copyWith(switch1: value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _getValueByCode(String code) {
|
||||||
|
switch (code) {
|
||||||
|
case 'switch_1':
|
||||||
|
return deviceStatus.switch1;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFetchBatchStatus(WallLightSwitchFetchBatchEvent event,
|
||||||
|
Emitter<WallLightSwitchState> emit) async {
|
||||||
|
emit(WallLightSwitchLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus =
|
||||||
|
WallLightStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(WallLightSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_timer?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onBatchControl(WallLightSwitchBatchControl event,
|
||||||
|
Emitter<WallLightSwitchState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.devicesIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
WallLightFactoryReset event, Emitter<WallLightSwitchState> emit) async {
|
||||||
|
emit(WallLightSwitchLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(WallLightSwitchError('Failed'));
|
||||||
|
} else {
|
||||||
|
emit(WallLightSwitchStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(WallLightSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
|
class WallLightSwitchEvent extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchFetchDeviceEvent extends WallLightSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
WallLightSwitchFetchDeviceEvent(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchControl extends WallLightSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
WallLightSwitchControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchFetchBatchEvent extends WallLightSwitchEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
WallLightSwitchFetchBatchEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchBatchControl extends WallLightSwitchEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
WallLightSwitchBatchControl(
|
||||||
|
{required this.devicesIds, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightFactoryReset extends WallLightSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
WallLightFactoryReset({required this.deviceId, required this.factoryReset});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart';
|
||||||
|
|
||||||
|
class WallLightSwitchState extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchInitial extends WallLightSwitchState {}
|
||||||
|
|
||||||
|
class WallLightSwitchLoading extends WallLightSwitchState {}
|
||||||
|
|
||||||
|
class WallLightSwitchStatusLoaded extends WallLightSwitchState {
|
||||||
|
final WallLightStatusModel status;
|
||||||
|
|
||||||
|
WallLightSwitchStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchError extends WallLightSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
WallLightSwitchError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchControlError extends WallLightSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
WallLightSwitchControlError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchBatchControlError extends WallLightSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
WallLightSwitchBatchControlError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class WallLightSwitchBatchStatusLoaded extends WallLightSwitchState {
|
||||||
|
final List<String> status;
|
||||||
|
|
||||||
|
WallLightSwitchBatchStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
|
class WallLightStatusModel {
|
||||||
|
final String uuid;
|
||||||
|
final bool switch1;
|
||||||
|
final int countDown;
|
||||||
|
|
||||||
|
WallLightStatusModel({
|
||||||
|
required this.uuid,
|
||||||
|
required this.switch1,
|
||||||
|
required this.countDown,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory WallLightStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||||
|
late bool switch1;
|
||||||
|
late int countDown;
|
||||||
|
|
||||||
|
for (var status in jsonList) {
|
||||||
|
switch (status.code) {
|
||||||
|
case 'switch_1':
|
||||||
|
switch1 = status.value ?? false;
|
||||||
|
break;
|
||||||
|
case 'countdown_1':
|
||||||
|
countDown = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WallLightStatusModel(
|
||||||
|
uuid: id,
|
||||||
|
switch1: switch1,
|
||||||
|
countDown: countDown,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
WallLightStatusModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
bool? switch1,
|
||||||
|
int? countDown,
|
||||||
|
}) {
|
||||||
|
return WallLightStatusModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
switch1: switch1 ?? this.switch1,
|
||||||
|
countDown: countDown ?? this.countDown,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
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/one_gang_switch/bloc/wall_light_switch_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.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';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class WallLightBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const WallLightBatchControlView({super.key, required this.deviceIds});
|
||||||
|
|
||||||
|
final List<String> deviceIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => WallLightSwitchBloc(deviceId: deviceIds.first)
|
||||||
|
..add(WallLightSwitchFetchBatchEvent(deviceIds)),
|
||||||
|
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is WallLightSwitchLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is WallLightSwitchStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is WallLightSwitchError ||
|
||||||
|
state is WallLightSwitchControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, WallLightStatusModel status) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return SizedBox(
|
||||||
|
child: 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: [
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch1,
|
||||||
|
code: 'switch_1',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<WallLightSwitchBloc>().add(
|
||||||
|
WallLightSwitchBatchControl(
|
||||||
|
devicesIds: deviceIds,
|
||||||
|
code: 'switch_1',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
version: 12,
|
||||||
|
),
|
||||||
|
FactoryResetWidget(
|
||||||
|
callFactoryReset: () {
|
||||||
|
context.read<WallLightSwitchBloc>().add(WallLightFactoryReset(
|
||||||
|
deviceId: status.uuid,
|
||||||
|
factoryReset: FactoryResetModel(devicesUuid: deviceIds)));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/bloc/wall_light_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/one_gang_switch/models/wall_light_status_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class WallLightDeviceControl extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const WallLightDeviceControl({super.key, required this.deviceId});
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => WallLightSwitchBloc(deviceId: deviceId)
|
||||||
|
..add(WallLightSwitchFetchDeviceEvent(deviceId)),
|
||||||
|
child: BlocBuilder<WallLightSwitchBloc, WallLightSwitchState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is WallLightSwitchLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is WallLightSwitchStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is WallLightSwitchError ||
|
||||||
|
state is WallLightSwitchControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, WallLightStatusModel status) {
|
||||||
|
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: [
|
||||||
|
const SizedBox(),
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch1,
|
||||||
|
code: 'switch_1',
|
||||||
|
deviceId: deviceId,
|
||||||
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<WallLightSwitchBloc>().add(WallLightSwitchControl(
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'switch_1',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class FactoryResetWidget extends StatefulWidget {
|
||||||
|
const FactoryResetWidget({super.key, required this.callFactoryReset});
|
||||||
|
|
||||||
|
final Function() callFactoryReset;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FactoryResetWidget> createState() => _FactoryResetWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FactoryResetWidgetState extends State<FactoryResetWidget> {
|
||||||
|
bool _showConfirmation = false;
|
||||||
|
|
||||||
|
void _toggleConfirmation() {
|
||||||
|
setState(() {
|
||||||
|
_showConfirmation = !_showConfirmation;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DeviceControlsContainer(
|
||||||
|
child: _showConfirmation
|
||||||
|
? Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Factory Reset',
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Are you sure?',
|
||||||
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: DefaultButton(
|
||||||
|
height: 20,
|
||||||
|
elevation: 0,
|
||||||
|
onPressed: _toggleConfirmation,
|
||||||
|
backgroundColor: ColorsManager.greyColor,
|
||||||
|
child: Text(
|
||||||
|
'Cancel',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Flexible(
|
||||||
|
child: DefaultButton(
|
||||||
|
height: 20,
|
||||||
|
elevation: 0,
|
||||||
|
onPressed: widget.callFactoryReset,
|
||||||
|
backgroundColor: ColorsManager.red,
|
||||||
|
child: Text(
|
||||||
|
'Reset',
|
||||||
|
style: context.textTheme.bodyMedium!.copyWith(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontSize: 12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: GestureDetector(
|
||||||
|
onTap: _toggleConfirmation,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
ClipOval(
|
||||||
|
child: Container(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.factoryReset,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Factory Reset',
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/device_controls_container.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
|
||||||
|
class FirmwareUpdateWidget extends StatelessWidget {
|
||||||
|
const FirmwareUpdateWidget(
|
||||||
|
{super.key, required String deviceId, required int version});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DeviceControlsContainer(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
ClipOval(
|
||||||
|
child: Container(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(12.0),
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
Assets.firmware,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Text(
|
||||||
|
'Firmware Update',
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_controls_based_code.dart';
|
||||||
|
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
class DeviceBatchControlDialog extends StatelessWidget
|
||||||
|
with RouteControlsBasedCode {
|
||||||
|
final List<AllDevicesModel> devices;
|
||||||
|
|
||||||
|
const DeviceBatchControlDialog({super.key, required this.devices});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
insetPadding: const EdgeInsets.all(20),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
child: SizedBox(
|
||||||
|
width: devices.length < 2 ? 500 : 800,
|
||||||
|
// height: context.screenHeight * 0.7,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const SizedBox(),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
getBatchDialogName(devices.first),
|
||||||
|
style: context.textTheme.titleLarge!.copyWith(
|
||||||
|
color: ColorsManager.dialogBlueTitle,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"Batch Control",
|
||||||
|
style: context.textTheme.bodySmall!.copyWith(
|
||||||
|
color: ColorsManager.dialogBlueTitle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 25,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.grey,
|
||||||
|
width: 1.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
padding: const EdgeInsets.all(1),
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
//// BUILD DEVICE CONTROLS
|
||||||
|
///
|
||||||
|
//// ROUTE TO SPECIFIC CONTROL VIEW BASED ON DEVICE CATEGORY
|
||||||
|
routeBatchControlsWidgets(devices: devices),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getBatchDialogName(AllDevicesModel device) {
|
||||||
|
/*
|
||||||
|
3G:
|
||||||
|
1G:
|
||||||
|
2G:
|
||||||
|
GW:
|
||||||
|
DL:
|
||||||
|
WPS:
|
||||||
|
CPS:
|
||||||
|
AC:
|
||||||
|
CUR:
|
||||||
|
WH:
|
||||||
|
*/
|
||||||
|
switch (device.productType) {
|
||||||
|
case '1G':
|
||||||
|
return "Smart Light Switch";
|
||||||
|
case '2G':
|
||||||
|
return "2Gang Light";
|
||||||
|
case '3G':
|
||||||
|
return "Living Room";
|
||||||
|
case 'GW':
|
||||||
|
return "GateWay";
|
||||||
|
case 'DL':
|
||||||
|
return "Door Lock";
|
||||||
|
case 'WPS':
|
||||||
|
return "White Presence Sensor";
|
||||||
|
case 'CPS':
|
||||||
|
return "Black Presence Sensor";
|
||||||
|
case 'CUR':
|
||||||
|
return "Smart Curtains";
|
||||||
|
case 'WH':
|
||||||
|
return "Smart Water Hater";
|
||||||
|
case 'AC':
|
||||||
|
return "Smart AC";
|
||||||
|
default:
|
||||||
|
return device.categoryName ?? 'Device Control';
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_controls_based_code.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_controls_based_code.dart';
|
||||||
|
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||||
|
|
||||||
class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||||
final AllDevicesModel device;
|
final AllDevicesModel device;
|
||||||
@ -22,7 +22,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 798,
|
width: 798,
|
||||||
height: context.screenHeight * 0.7,
|
// height: context.screenHeight * 0.7,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
@ -87,13 +87,12 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
children: [
|
children: [
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Product Name:', device.categoryName ?? 'N/A'),
|
_buildInfoRow('Product Name:', device.productName ?? 'N/A'),
|
||||||
_buildInfoRow('Device ID:', device.uuid ?? ''),
|
_buildInfoRow('Device ID:', device.uuid ?? ''),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(children: [
|
TableRow(children: [
|
||||||
_buildInfoRow('Virtual Address:',
|
_buildInfoRow('Virtual Address:', device.ip ?? '-'),
|
||||||
'Area - Street 1 - Building 1 - First Floor'),
|
|
||||||
const SizedBox.shrink(),
|
const SizedBox.shrink(),
|
||||||
]),
|
]),
|
||||||
TableRow(
|
TableRow(
|
||||||
@ -104,14 +103,38 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
|||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Installation Date and Time:', '09/08/2024 13:30'),
|
_buildInfoRow(
|
||||||
const SizedBox.shrink(),
|
'Installation Date and Time:',
|
||||||
|
formatDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
((device.createTime ?? 0) * 1000),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildInfoRow(
|
||||||
|
'Battery Level:',
|
||||||
|
device.batteryLevel != null
|
||||||
|
? '${device.batteryLevel ?? 0}%'
|
||||||
|
: "-",
|
||||||
|
statusColor: device.batteryLevel != null
|
||||||
|
? (device.batteryLevel! < 20
|
||||||
|
? ColorsManager.red
|
||||||
|
: ColorsManager.green)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TableRow(
|
TableRow(
|
||||||
children: [
|
children: [
|
||||||
_buildInfoRow('Status:', 'Online', statusColor: Colors.green),
|
_buildInfoRow('Status:', 'Online', statusColor: Colors.green),
|
||||||
_buildInfoRow('Last Offline Date and Time:', '-'),
|
_buildInfoRow(
|
||||||
|
'Last Offline Date and Time:',
|
||||||
|
formatDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(
|
||||||
|
((device.updateTime ?? 0) * 1000),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -4,20 +4,25 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_rep
|
|||||||
import 'package:syncrow_web/pages/device_managment/shared/table/table_cell_widget.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/table/table_cell_widget.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/table/table_header.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/table/table_header.dart';
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
class ReportsTable extends StatelessWidget {
|
class ReportsTable extends StatelessWidget {
|
||||||
final DeviceReport report;
|
final DeviceReport report;
|
||||||
final String? thirdColumnTitle;
|
final String? thirdColumnTitle;
|
||||||
final String? thirdColumnDescription;
|
final String? thirdColumnDescription;
|
||||||
final Function(int index) onRowTap;
|
final Function(int index) onRowTap;
|
||||||
final VoidCallback onClose;
|
final VoidCallback onClose;
|
||||||
|
bool? hideValueShowDescription;
|
||||||
|
bool? mainDoorSensor;
|
||||||
|
|
||||||
const ReportsTable({
|
ReportsTable({
|
||||||
super.key,
|
super.key,
|
||||||
required this.report,
|
required this.report,
|
||||||
required this.onRowTap,
|
required this.onRowTap,
|
||||||
required this.onClose,
|
required this.onClose,
|
||||||
this.thirdColumnTitle,
|
this.thirdColumnTitle,
|
||||||
this.thirdColumnDescription,
|
this.thirdColumnDescription,
|
||||||
|
this.hideValueShowDescription,
|
||||||
|
this.mainDoorSensor,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -57,10 +62,21 @@ class ReportsTable extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
TableCellWidget(value: date),
|
TableCellWidget(value: date),
|
||||||
TableCellWidget(value: time),
|
TableCellWidget(value: time),
|
||||||
TableCellWidget(
|
hideValueShowDescription == true
|
||||||
value: '${data.value!} ${thirdColumnDescription ?? ''}',
|
? TableCellWidget(
|
||||||
onTap: () => onRowTap(index),
|
value: (mainDoorSensor != null &&
|
||||||
),
|
mainDoorSensor == true)
|
||||||
|
? data.value == 'true'
|
||||||
|
? 'Open'
|
||||||
|
: 'Close'
|
||||||
|
: thirdColumnDescription ?? '',
|
||||||
|
onTap: () => onRowTap(index),
|
||||||
|
)
|
||||||
|
: TableCellWidget(
|
||||||
|
value:
|
||||||
|
'${data.value!} ${thirdColumnDescription ?? ''}',
|
||||||
|
onTap: () => onRowTap(index),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
90
lib/pages/device_managment/shared/toggle_widget.dart
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.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 ToggleWidget extends StatelessWidget {
|
||||||
|
final bool value;
|
||||||
|
final String code;
|
||||||
|
final String deviceId;
|
||||||
|
final String label;
|
||||||
|
final String? icon;
|
||||||
|
final Widget? labelWidget;
|
||||||
|
final Function(dynamic value) onChange;
|
||||||
|
|
||||||
|
const ToggleWidget({
|
||||||
|
super.key,
|
||||||
|
required this.value,
|
||||||
|
required this.code,
|
||||||
|
required this.deviceId,
|
||||||
|
required this.label,
|
||||||
|
required this.onChange,
|
||||||
|
this.icon,
|
||||||
|
this.labelWidget,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||||
|
border: Border.all(color: ColorsManager.boxDivider),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 12),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
icon == '-1'
|
||||||
|
? const SizedBox(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
)
|
||||||
|
: ClipOval(
|
||||||
|
child: Container(
|
||||||
|
height: 60,
|
||||||
|
width: 60,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
icon ?? Assets.lightPulp,
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
Container(
|
||||||
|
height: 20,
|
||||||
|
width: 35,
|
||||||
|
padding: const EdgeInsets.only(right: 16, top: 10),
|
||||||
|
child: CupertinoSwitch(
|
||||||
|
value: value,
|
||||||
|
activeColor: ColorsManager.dialogBlueTitle,
|
||||||
|
onChanged: onChange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
labelWidget ??
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: ColorsManager.blackColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import 'dart:async';
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
|
||||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
@ -16,23 +17,29 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
|
|||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
LivingRoomBloc({required this.deviceId}) : super(LivingRoomInitial()) {
|
LivingRoomBloc({required this.deviceId}) : super(LivingRoomInitial()) {
|
||||||
on<LivingRoomFetchDeviceStatus>(_onFetchDeviceStatus);
|
on<LivingRoomFetchDeviceStatusEvent>(_onFetchDeviceStatus);
|
||||||
on<LivingRoomControl>(_livingRoomControl);
|
on<LivingRoomControl>(_livingRoomControl);
|
||||||
|
on<LivingRoomBatchControl>(_livingRoomBatchControl);
|
||||||
|
on<LivingRoomFetchBatchEvent>(_livingRoomFetchBatchControl);
|
||||||
|
on<LivingRoomFactoryResetEvent>(_livingRoomFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _onFetchDeviceStatus(
|
FutureOr<void> _onFetchDeviceStatus(LivingRoomFetchDeviceStatusEvent event,
|
||||||
LivingRoomFetchDeviceStatus event, Emitter<LivingRoomState> emit) async {
|
Emitter<LivingRoomState> emit) async {
|
||||||
emit(LivingRoomDeviceStatusLoading());
|
emit(LivingRoomDeviceStatusLoading());
|
||||||
try {
|
try {
|
||||||
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
final status =
|
||||||
deviceStatus = LivingRoomStatusModel.fromJson(event.deviceId, status.status);
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
|
deviceStatus =
|
||||||
|
LivingRoomStatusModel.fromJson(event.deviceId, status.status);
|
||||||
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(LivingRoomDeviceManagementError(e.toString()));
|
emit(LivingRoomDeviceManagementError(e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<void> _livingRoomControl(LivingRoomControl event, Emitter<LivingRoomState> emit) async {
|
FutureOr<void> _livingRoomControl(
|
||||||
|
LivingRoomControl event, Emitter<LivingRoomState> emit) async {
|
||||||
final oldValue = _getValueByCode(event.code);
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
_updateLocalValue(event.code, event.value);
|
_updateLocalValue(event.code, event.value);
|
||||||
@ -45,37 +52,52 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
|
|||||||
value: event.value,
|
value: event.value,
|
||||||
oldValue: oldValue,
|
oldValue: oldValue,
|
||||||
emit: emit,
|
emit: emit,
|
||||||
|
isBatch: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runDebounce({
|
Future<void> _runDebounce({
|
||||||
required String deviceId,
|
required dynamic deviceId,
|
||||||
required String code,
|
required String code,
|
||||||
required dynamic value,
|
required dynamic value,
|
||||||
required dynamic oldValue,
|
required dynamic oldValue,
|
||||||
required Emitter<LivingRoomState> emit,
|
required Emitter<LivingRoomState> emit,
|
||||||
|
required bool isBatch,
|
||||||
}) async {
|
}) async {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
}
|
}
|
||||||
_timer = Timer(const Duration(seconds: 1), () async {
|
_timer = Timer(const Duration(seconds: 1), () async {
|
||||||
try {
|
try {
|
||||||
final response =
|
late bool response;
|
||||||
await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value));
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
if (!response) {
|
if (!response) {
|
||||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_revertValueAndEmit(deviceId, code, oldValue, emit);
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _revertValueAndEmit(
|
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
|
||||||
String deviceId, String code, dynamic oldValue, Emitter<LivingRoomState> emit) {
|
Emitter<LivingRoomState> emit) {
|
||||||
_updateLocalValue(code, oldValue);
|
_updateLocalValue(code, oldValue);
|
||||||
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||||
emit(const LivingRoomControlError('Failed to control the device.'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateLocalValue(String code, dynamic value) {
|
void _updateLocalValue(String code, dynamic value) {
|
||||||
@ -113,4 +135,54 @@ class LivingRoomBloc extends Bloc<LivingRoomEvent, LivingRoomState> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _livingRoomFetchBatchControl(
|
||||||
|
LivingRoomFetchBatchEvent event, Emitter<LivingRoomState> emit) async {
|
||||||
|
emit(LivingRoomDeviceStatusLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus =
|
||||||
|
LivingRoomStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||||
|
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(LivingRoomDeviceManagementError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _livingRoomBatchControl(
|
||||||
|
LivingRoomBatchControl event, Emitter<LivingRoomState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.devicesIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _livingRoomFactoryReset(
|
||||||
|
LivingRoomFactoryResetEvent event, Emitter<LivingRoomState> emit) async {
|
||||||
|
emit(LivingRoomDeviceStatusLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.uuid,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const LivingRoomDeviceManagementError('Failed'));
|
||||||
|
} else {
|
||||||
|
emit(LivingRoomDeviceStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(LivingRoomDeviceManagementError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,25 @@ sealed class LivingRoomEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class LivingRoomFetchDeviceStatus extends LivingRoomEvent {
|
class LivingRoomFetchDeviceStatusEvent extends LivingRoomEvent {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const LivingRoomFetchDeviceStatus(this.deviceId);
|
const LivingRoomFetchDeviceStatusEvent(this.deviceId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId];
|
List<Object> get props => [deviceId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//LivingRoomFetchBatchStatus
|
||||||
|
class LivingRoomFetchBatchEvent extends LivingRoomEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
const LivingRoomFetchBatchEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
class LivingRoomControl extends LivingRoomEvent {
|
class LivingRoomControl extends LivingRoomEvent {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
final String code;
|
final String code;
|
||||||
@ -27,3 +37,24 @@ class LivingRoomControl extends LivingRoomEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [deviceId, code, value];
|
List<Object> get props => [deviceId, code, value];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LivingRoomBatchControl extends LivingRoomEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
const LivingRoomBatchControl(
|
||||||
|
{required this.devicesIds, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class LivingRoomFactoryResetEvent extends LivingRoomEvent {
|
||||||
|
final String uuid;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
const LivingRoomFactoryResetEvent(this.uuid, this.factoryReset);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [uuid, factoryReset];
|
||||||
|
}
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
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/shared/batch_control/factory_reset.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class LivingRoomBatchControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const LivingRoomBatchControlsView({super.key, required this.deviceIds});
|
||||||
|
|
||||||
|
final List<String> deviceIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => LivingRoomBloc(deviceId: deviceIds.first)
|
||||||
|
..add(LivingRoomFetchBatchEvent(deviceIds)),
|
||||||
|
child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is LivingRoomDeviceStatusLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is LivingRoomDeviceStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is LivingRoomDeviceManagementError ||
|
||||||
|
state is LivingRoomControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, LivingRoomStatusModel status) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return SizedBox(
|
||||||
|
child: 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: [
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch1,
|
||||||
|
code: 'switch_1',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomBatchControl(
|
||||||
|
devicesIds: deviceIds,
|
||||||
|
code: 'switch_1',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch2,
|
||||||
|
code: 'switch_2',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Ceiling Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomBatchControl(
|
||||||
|
devicesIds: deviceIds,
|
||||||
|
code: 'switch_2',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch3,
|
||||||
|
code: 'switch_2',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Spotlight',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomBatchControl(
|
||||||
|
devicesIds: deviceIds,
|
||||||
|
code: 'switch_3',
|
||||||
|
value: value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
version: 12,
|
||||||
|
),
|
||||||
|
FactoryResetWidget(callFactoryReset: () {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomFactoryResetEvent(
|
||||||
|
status.uuid,
|
||||||
|
FactoryResetModel(devicesUuid: deviceIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,26 +2,28 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/bloc/living_room_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
|
import 'package:syncrow_web/pages/device_managment/three_gang_switch/models/living_room_model.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/three_gang_switch/widgets/living_toggle_widget.dart';
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
class LivingRoomDeviceControl extends StatelessWidget with HelperResponsiveLayout {
|
class LivingRoomDeviceControlsView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
final String deviceId;
|
final String deviceId;
|
||||||
|
|
||||||
const LivingRoomDeviceControl({super.key, required this.deviceId});
|
const LivingRoomDeviceControlsView({super.key, required this.deviceId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (context) =>
|
create: (context) => LivingRoomBloc(deviceId: deviceId)
|
||||||
LivingRoomBloc(deviceId: deviceId)..add(LivingRoomFetchDeviceStatus(deviceId)),
|
..add(LivingRoomFetchDeviceStatusEvent(deviceId)),
|
||||||
child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
|
child: BlocBuilder<LivingRoomBloc, LivingRoomState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state is LivingRoomDeviceStatusLoading) {
|
if (state is LivingRoomDeviceStatusLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is LivingRoomDeviceStatusLoaded) {
|
} else if (state is LivingRoomDeviceStatusLoaded) {
|
||||||
return _buildStatusControls(context, state.status);
|
return _buildStatusControls(context, state.status);
|
||||||
} else if (state is LivingRoomDeviceManagementError || state is LivingRoomControlError) {
|
} else if (state is LivingRoomDeviceManagementError ||
|
||||||
|
state is LivingRoomControlError) {
|
||||||
return const Center(child: Text('Error fetching status'));
|
return const Center(child: Text('Error fetching status'));
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@ -31,7 +33,8 @@ class LivingRoomDeviceControl extends StatelessWidget with HelperResponsiveLayou
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatusControls(BuildContext context, LivingRoomStatusModel status) {
|
Widget _buildStatusControls(
|
||||||
|
BuildContext context, LivingRoomStatusModel status) {
|
||||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
final isLarge = isLargeScreenSize(context);
|
final isLarge = isLargeScreenSize(context);
|
||||||
final isMedium = isMediumScreenSize(context);
|
final isMedium = isMediumScreenSize(context);
|
||||||
@ -55,18 +58,36 @@ class LivingRoomDeviceControl extends StatelessWidget with HelperResponsiveLayou
|
|||||||
code: 'switch_1',
|
code: 'switch_1',
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
label: 'Wall Light',
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomControl(
|
||||||
|
deviceId: deviceId, code: 'switch_1', value: value),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ToggleWidget(
|
ToggleWidget(
|
||||||
value: status.switch2,
|
value: status.switch2,
|
||||||
code: 'switch_2',
|
code: 'switch_2',
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
label: 'Ceiling Light',
|
label: 'Ceiling Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomControl(
|
||||||
|
deviceId: deviceId, code: 'switch_2', value: value),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ToggleWidget(
|
ToggleWidget(
|
||||||
value: status.switch3,
|
value: status.switch3,
|
||||||
code: 'switch_3',
|
code: 'switch_3',
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
label: 'Spotlight',
|
label: 'Spotlight',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<LivingRoomBloc>().add(
|
||||||
|
LivingRoomControl(
|
||||||
|
deviceId: deviceId, code: 'switch_3', value: value),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,177 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart';
|
||||||
|
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||||
|
|
||||||
|
class TwoGangSwitchBloc extends Bloc<TwoGangSwitchEvent, TwoGangSwitchState> {
|
||||||
|
TwoGangSwitchBloc({required this.deviceId}) : super(TwoGangSwitchInitial()) {
|
||||||
|
on<TwoGangSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||||
|
on<TwoGangSwitchControl>(_onControl);
|
||||||
|
on<TwoGangSwitchFetchBatchEvent>(_onFetchBatchStatus);
|
||||||
|
on<TwoGangSwitchBatchControl>(_onBatchControl);
|
||||||
|
on<TwoGangFactoryReset>(_onFactoryReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
late TwoGangStatusModel deviceStatus;
|
||||||
|
final String deviceId;
|
||||||
|
Timer? _timer;
|
||||||
|
|
||||||
|
FutureOr<void> _onFetchDeviceStatus(TwoGangSwitchFetchDeviceEvent event,
|
||||||
|
Emitter<TwoGangSwitchState> emit) async {
|
||||||
|
emit(TwoGangSwitchLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||||
|
|
||||||
|
deviceStatus = TwoGangStatusModel.fromJson(event.deviceId, status.status);
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(TwoGangSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onControl(
|
||||||
|
TwoGangSwitchControl event, Emitter<TwoGangSwitchState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _runDebounce({
|
||||||
|
required dynamic deviceId,
|
||||||
|
required String code,
|
||||||
|
required bool value,
|
||||||
|
required bool oldValue,
|
||||||
|
required Emitter<TwoGangSwitchState> emit,
|
||||||
|
required bool isBatch,
|
||||||
|
}) async {
|
||||||
|
late String id;
|
||||||
|
|
||||||
|
if (deviceId is List) {
|
||||||
|
id = deviceId.first;
|
||||||
|
} else {
|
||||||
|
id = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timer != null) {
|
||||||
|
_timer!.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Timer(const Duration(milliseconds: 500), () async {
|
||||||
|
try {
|
||||||
|
late bool response;
|
||||||
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_revertValueAndEmit(id, code, oldValue, emit);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _revertValueAndEmit(String deviceId, String code, bool oldValue,
|
||||||
|
Emitter<TwoGangSwitchState> emit) {
|
||||||
|
_updateLocalValue(code, oldValue);
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateLocalValue(String code, bool value) {
|
||||||
|
if (code == 'switch_1') {
|
||||||
|
deviceStatus = deviceStatus.copyWith(switch1: value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 'switch_2') {
|
||||||
|
deviceStatus = deviceStatus.copyWith(switch2: value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _getValueByCode(String code) {
|
||||||
|
switch (code) {
|
||||||
|
case 'switch_1':
|
||||||
|
return deviceStatus.switch1;
|
||||||
|
case 'switch_2':
|
||||||
|
return deviceStatus.switch2;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onFetchBatchStatus(TwoGangSwitchFetchBatchEvent event,
|
||||||
|
Emitter<TwoGangSwitchState> emit) async {
|
||||||
|
emit(TwoGangSwitchLoading());
|
||||||
|
try {
|
||||||
|
final status =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus =
|
||||||
|
TwoGangStatusModel.fromJson(event.devicesIds.first, status.status);
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(TwoGangSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> close() {
|
||||||
|
_timer?.cancel();
|
||||||
|
return super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onBatchControl(
|
||||||
|
TwoGangSwitchBatchControl event, Emitter<TwoGangSwitchState> emit) async {
|
||||||
|
final oldValue = _getValueByCode(event.code);
|
||||||
|
|
||||||
|
_updateLocalValue(event.code, event.value);
|
||||||
|
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
|
||||||
|
await _runDebounce(
|
||||||
|
deviceId: event.deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
oldValue: oldValue,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
TwoGangFactoryReset event, Emitter<TwoGangSwitchState> emit) async {
|
||||||
|
emit(TwoGangSwitchLoading());
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(TwoGangSwitchError('Failed'));
|
||||||
|
} else {
|
||||||
|
emit(TwoGangSwitchStatusLoaded(deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(TwoGangSwitchError(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||||
|
|
||||||
|
class TwoGangSwitchEvent extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchFetchDeviceEvent extends TwoGangSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
TwoGangSwitchFetchDeviceEvent(this.deviceId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchControl extends TwoGangSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
TwoGangSwitchControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchFetchBatchEvent extends TwoGangSwitchEvent {
|
||||||
|
final List<String> devicesIds;
|
||||||
|
|
||||||
|
TwoGangSwitchFetchBatchEvent(this.devicesIds);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [devicesIds];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchBatchControl extends TwoGangSwitchEvent {
|
||||||
|
final List<String> deviceId;
|
||||||
|
final String code;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
TwoGangSwitchBatchControl(
|
||||||
|
{required this.deviceId, required this.code, required this.value});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, code, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangFactoryReset extends TwoGangSwitchEvent {
|
||||||
|
final String deviceId;
|
||||||
|
final FactoryResetModel factoryReset;
|
||||||
|
|
||||||
|
TwoGangFactoryReset({required this.deviceId, required this.factoryReset});
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [deviceId, factoryReset];
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart';
|
||||||
|
|
||||||
|
class TwoGangSwitchState extends Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchInitial extends TwoGangSwitchState {}
|
||||||
|
|
||||||
|
class TwoGangSwitchLoading extends TwoGangSwitchState {}
|
||||||
|
|
||||||
|
class TwoGangSwitchStatusLoaded extends TwoGangSwitchState {
|
||||||
|
final TwoGangStatusModel status;
|
||||||
|
|
||||||
|
TwoGangSwitchStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchError extends TwoGangSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
TwoGangSwitchError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchControlError extends TwoGangSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
TwoGangSwitchControlError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchBatchControlError extends TwoGangSwitchState {
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
TwoGangSwitchBatchControlError(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [message];
|
||||||
|
}
|
||||||
|
|
||||||
|
class TwoGangSwitchBatchStatusLoaded extends TwoGangSwitchState {
|
||||||
|
final List<String> status;
|
||||||
|
|
||||||
|
TwoGangSwitchBatchStatusLoaded(this.status);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object> get props => [status];
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||||
|
|
||||||
|
class TwoGangStatusModel {
|
||||||
|
final String uuid;
|
||||||
|
final bool switch1;
|
||||||
|
final bool switch2;
|
||||||
|
final int countDown;
|
||||||
|
final int countDown2;
|
||||||
|
|
||||||
|
TwoGangStatusModel({
|
||||||
|
required this.uuid,
|
||||||
|
required this.switch1,
|
||||||
|
required this.switch2,
|
||||||
|
required this.countDown,
|
||||||
|
required this.countDown2,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory TwoGangStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||||
|
late bool switch1;
|
||||||
|
late bool switch2;
|
||||||
|
late int countDown;
|
||||||
|
late int countDown2;
|
||||||
|
|
||||||
|
for (var status in jsonList) {
|
||||||
|
switch (status.code) {
|
||||||
|
case 'switch_1':
|
||||||
|
switch1 = status.value ?? false;
|
||||||
|
break;
|
||||||
|
case 'countdown_1':
|
||||||
|
countDown = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
case 'switch_2':
|
||||||
|
switch2 = status.value ?? false;
|
||||||
|
break;
|
||||||
|
case 'countdown_2':
|
||||||
|
countDown2 = status.value ?? 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TwoGangStatusModel(
|
||||||
|
uuid: id,
|
||||||
|
switch1: switch1,
|
||||||
|
countDown: countDown,
|
||||||
|
switch2: switch2,
|
||||||
|
countDown2: countDown2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TwoGangStatusModel copyWith({
|
||||||
|
String? uuid,
|
||||||
|
bool? switch1,
|
||||||
|
int? countDown,
|
||||||
|
bool? switch2,
|
||||||
|
int? countDown2,
|
||||||
|
}) {
|
||||||
|
return TwoGangStatusModel(
|
||||||
|
uuid: uuid ?? this.uuid,
|
||||||
|
switch1: switch1 ?? this.switch1,
|
||||||
|
countDown: countDown ?? this.countDown,
|
||||||
|
switch2: switch2 ?? this.switch2,
|
||||||
|
countDown2: countDown2 ?? this.countDown2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
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/shared/batch_control/factory_reset.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/batch_control/firmware_update.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class TwoGangBatchControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
const TwoGangBatchControlView({super.key, required this.deviceIds});
|
||||||
|
|
||||||
|
final List<String> deviceIds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => TwoGangSwitchBloc(deviceId: deviceIds.first)
|
||||||
|
..add(TwoGangSwitchFetchBatchEvent(deviceIds)),
|
||||||
|
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is TwoGangSwitchLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is TwoGangSwitchStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is TwoGangSwitchError ||
|
||||||
|
state is TwoGangSwitchControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context, TwoGangStatusModel status) {
|
||||||
|
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||||
|
final isLarge = isLargeScreenSize(context);
|
||||||
|
final isMedium = isMediumScreenSize(context);
|
||||||
|
return SizedBox(
|
||||||
|
child: 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: [
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch1,
|
||||||
|
code: 'switch_1',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<TwoGangSwitchBloc>().add(TwoGangSwitchBatchControl(
|
||||||
|
deviceId: deviceIds,
|
||||||
|
code: 'switch_1',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ToggleWidget(
|
||||||
|
value: status.switch2,
|
||||||
|
code: 'switch_2',
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
label: 'Ceiling Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<TwoGangSwitchBloc>().add(TwoGangSwitchBatchControl(
|
||||||
|
deviceId: deviceIds,
|
||||||
|
code: 'switch_2',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
FirmwareUpdateWidget(
|
||||||
|
deviceId: deviceIds.first,
|
||||||
|
version: 12,
|
||||||
|
),
|
||||||
|
FactoryResetWidget(callFactoryReset: () {
|
||||||
|
context.read<TwoGangSwitchBloc>().add(
|
||||||
|
TwoGangFactoryReset(
|
||||||
|
deviceId: status.uuid,
|
||||||
|
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_bloc.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_event.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/bloc/two_gang_switch_state.dart';
|
||||||
|
import 'package:syncrow_web/pages/device_managment/two_gang_switch/models/two_gang_status_model.dart';
|
||||||
|
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||||
|
|
||||||
|
class TwoGangDeviceControlView extends StatelessWidget
|
||||||
|
with HelperResponsiveLayout {
|
||||||
|
final String deviceId;
|
||||||
|
|
||||||
|
const TwoGangDeviceControlView({super.key, required this.deviceId});
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocProvider(
|
||||||
|
create: (context) => TwoGangSwitchBloc(deviceId: deviceId)
|
||||||
|
..add(TwoGangSwitchFetchDeviceEvent(deviceId)),
|
||||||
|
child: BlocBuilder<TwoGangSwitchBloc, TwoGangSwitchState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is TwoGangSwitchLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (state is TwoGangSwitchStatusLoaded) {
|
||||||
|
return _buildStatusControls(context, state.status);
|
||||||
|
} else if (state is TwoGangSwitchError ||
|
||||||
|
state is TwoGangSwitchControlError) {
|
||||||
|
return const Center(child: Text('Error fetching status'));
|
||||||
|
} else {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusControls(BuildContext context, TwoGangStatusModel status) {
|
||||||
|
return Center(
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.center,
|
||||||
|
spacing: 12,
|
||||||
|
runSpacing: 12,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
child: ToggleWidget(
|
||||||
|
value: status.switch1,
|
||||||
|
code: 'switch_1',
|
||||||
|
deviceId: deviceId,
|
||||||
|
label: 'Wall Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<TwoGangSwitchBloc>().add(TwoGangSwitchControl(
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'switch_1',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 200,
|
||||||
|
child: ToggleWidget(
|
||||||
|
value: status.switch2,
|
||||||
|
code: 'switch_2',
|
||||||
|
deviceId: deviceId,
|
||||||
|
label: 'Ceiling Light',
|
||||||
|
onChange: (value) {
|
||||||
|
context.read<TwoGangSwitchBloc>().add(TwoGangSwitchControl(
|
||||||
|
deviceId: deviceId,
|
||||||
|
code: 'switch_2',
|
||||||
|
value: value,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -12,15 +12,18 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
WallSensorBloc({required this.deviceId}) : super(WallSensorInitialState()) {
|
WallSensorBloc({required this.deviceId}) : super(WallSensorInitialState()) {
|
||||||
on<WallSensorInitialEvent>(_fetchWallSensorStatus);
|
on<WallSensorFetchStatusEvent>(_fetchWallSensorStatus);
|
||||||
|
on<WallSensorFetchBatchStatusEvent>(_fetchWallSensorBatchControl);
|
||||||
on<WallSensorChangeValueEvent>(_changeValue);
|
on<WallSensorChangeValueEvent>(_changeValue);
|
||||||
|
on<WallSensorBatchControlEvent>(_onBatchControl);
|
||||||
on<GetDeviceReportsEvent>(_getDeviceReports);
|
on<GetDeviceReportsEvent>(_getDeviceReports);
|
||||||
on<ShowDescriptionEvent>(_showDescription);
|
on<ShowDescriptionEvent>(_showDescription);
|
||||||
on<BackToGridViewEvent>(_backToGridView);
|
on<BackToGridViewEvent>(_backToGridView);
|
||||||
|
on<WallSensorFactoryResetEvent>(_onFactoryReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _fetchWallSensorStatus(
|
void _fetchWallSensorStatus(
|
||||||
WallSensorInitialEvent event, Emitter<WallSensorState> emit) async {
|
WallSensorFetchStatusEvent event, Emitter<WallSensorState> emit) async {
|
||||||
emit(WallSensorLoadingInitialState());
|
emit(WallSensorLoadingInitialState());
|
||||||
try {
|
try {
|
||||||
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
var response = await DevicesManagementApi().getDeviceStatus(deviceId);
|
||||||
@ -33,6 +36,21 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fetch batch status
|
||||||
|
FutureOr<void> _fetchWallSensorBatchControl(
|
||||||
|
WallSensorFetchBatchStatusEvent event,
|
||||||
|
Emitter<WallSensorState> emit) async {
|
||||||
|
emit(WallSensorLoadingInitialState());
|
||||||
|
try {
|
||||||
|
var response =
|
||||||
|
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||||
|
deviceStatus = WallSensorModel.fromJson(response.status);
|
||||||
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
|
} catch (e) {
|
||||||
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// _listenToChanges() {
|
// _listenToChanges() {
|
||||||
// try {
|
// try {
|
||||||
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
|
// DatabaseReference ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
|
||||||
@ -66,28 +84,63 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
}
|
}
|
||||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
await _runDeBouncer(
|
await _runDeBouncer(
|
||||||
deviceId: deviceId, code: event.code, value: event.value);
|
deviceId: deviceId,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
isBatch: false,
|
||||||
|
emit: emit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onBatchControl(
|
||||||
|
WallSensorBatchControlEvent event, Emitter<WallSensorState> emit) async {
|
||||||
|
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||||
|
if (event.code == 'far_detection') {
|
||||||
|
deviceStatus.farDetection = event.value;
|
||||||
|
} else if (event.code == 'motionless_sensitivity') {
|
||||||
|
deviceStatus.motionlessSensitivity = event.value;
|
||||||
|
} else if (event.code == 'motion_sensitivity_value') {
|
||||||
|
deviceStatus.motionSensitivity = event.value;
|
||||||
|
} else if (event.code == 'no_one_time') {
|
||||||
|
deviceStatus.noBodyTime = event.value;
|
||||||
|
}
|
||||||
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
|
await _runDeBouncer(
|
||||||
|
deviceId: event.deviceIds,
|
||||||
|
code: event.code,
|
||||||
|
value: event.value,
|
||||||
|
emit: emit,
|
||||||
|
isBatch: true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_runDeBouncer({
|
_runDeBouncer({
|
||||||
required String deviceId,
|
required dynamic deviceId,
|
||||||
required String code,
|
required String code,
|
||||||
required dynamic value,
|
required dynamic value,
|
||||||
|
required Emitter<WallSensorState> emit,
|
||||||
|
required bool isBatch,
|
||||||
}) {
|
}) {
|
||||||
if (_timer != null) {
|
if (_timer != null) {
|
||||||
_timer!.cancel();
|
_timer!.cancel();
|
||||||
}
|
}
|
||||||
_timer = Timer(const Duration(seconds: 1), () async {
|
_timer = Timer(const Duration(seconds: 1), () async {
|
||||||
try {
|
try {
|
||||||
final response = await DevicesManagementApi()
|
late bool response;
|
||||||
.deviceControl(deviceId, Status(code: code, value: value));
|
if (isBatch) {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceBatchControl(deviceId, code, value);
|
||||||
|
} else {
|
||||||
|
response = await DevicesManagementApi()
|
||||||
|
.deviceControl(deviceId, Status(code: code, value: value));
|
||||||
|
}
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
add(WallSensorInitialEvent());
|
add(WallSensorFetchStatusEvent());
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
await Future.delayed(const Duration(milliseconds: 500));
|
await Future.delayed(const Duration(milliseconds: 500));
|
||||||
add(WallSensorInitialEvent());
|
add(WallSensorFetchStatusEvent());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -99,7 +152,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
try {
|
try {
|
||||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
||||||
.then((value) {
|
.then((value) {
|
||||||
emit(DeviceReportsState(deviceReport: value, code:event.code));
|
emit(DeviceReportsState(deviceReport: value, code: event.code));
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(DeviceReportsFailedState(error: e.toString()));
|
emit(DeviceReportsFailedState(error: e.toString()));
|
||||||
@ -116,4 +169,22 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
|||||||
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
|
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
|
||||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutureOr<void> _onFactoryReset(
|
||||||
|
WallSensorFactoryResetEvent event, Emitter<WallSensorState> emit) async {
|
||||||
|
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||||
|
try {
|
||||||
|
final response = await DevicesManagementApi().factoryReset(
|
||||||
|
event.factoryReset,
|
||||||
|
event.deviceId,
|
||||||
|
);
|
||||||
|
if (!response) {
|
||||||
|
emit(const WallSensorFailedState(error: 'Failed'));
|
||||||
|
} else {
|
||||||
|
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
emit(WallSensorFailedState(error: e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|