Compare commits

..

36 Commits

Author SHA1 Message Date
05d784ec11 Merge branch 'dev' of https://github.com/SyncrowIOT/web into analytics-empty-state 2025-06-30 12:53:42 +03:00
9ebf474a60 analytics-empty-state. 2025-06-30 12:52:22 +03:00
af48bbead5 fix-occupancy-devices-bug (#318)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Description

handle more cases when decoding analytics devices.

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-06-30 11:28:38 +03:00
3c80724c1e Sp 1707 fe preferences calibration fix UI (#317)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1707](https://syncrow.atlassian.net/browse/SP-1707)

## Description

fix the UI as wanted in Figma 

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1707]:
https://syncrow.atlassian.net/browse/SP-1707?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-30 11:21:26 +03:00
db05331e9a update AnalyticsChartEmptyStateWidget to use new icons. 2025-06-30 11:18:08 +03:00
cdc76c2c8e handle more cases when decoding analytics devices. 2025-06-30 11:04:22 +03:00
44c88fb1c4 added empty charts icons. 2025-06-30 10:54:33 +03:00
dfb120e7cf [FE] Smart curtain module device Icon and other devices icons are appearing as a sensor Icon on Add device dialog on space management (#316)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1804](https://syncrow.atlassian.net/browse/SP-1804)

## Description

add the new product types and map to the icons

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1804]:
https://syncrow.atlassian.net/browse/SP-1804?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-30 10:47:37 +03:00
4d51321675 add the new devices to mapIconToProduct func 2025-06-30 10:05:15 +03:00
b5e7776ccb Sp 1705 fe create scheduling UI fixes (#315)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1705](https://syncrow.atlassian.net/browse/SP-1705)

## Description

i fixed edit and insure integrating with backend for schedule and remove
countdown from UI if category cur module

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1705]:
https://syncrow.atlassian.net/browse/SP-1705?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-30 09:34:11 +03:00
32938404dd PR requested changes 2025-06-30 09:28:09 +03:00
0cfd58d820 fix to send fit data to integrate with API (was true and false)now cur module send close and open with control key 2025-06-30 08:56:42 +03:00
d4625a8f04 fix edit to accept string of cur module 2025-06-30 08:45:18 +03:00
9f24606613 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1705-fe-create-scheduling-ui-fixes 2025-06-30 08:28:40 +03:00
e87dffd76b when it is CUR module there is no countdown and other selector 2025-06-30 08:28:19 +03:00
0c220a1f34 [FE] UI Enhancement: Update Confirmation Dialog on "Create Visitor Password" Flow (#310)
… the requested ticket)

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1660](https://syncrow.atlassian.net/browse/SP-1660)

## Description
enhance UI in create visitor insure dialog as wanted in Ticket (in figma
not updated yet)

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1660]:
https://syncrow.atlassian.net/browse/SP-1660?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 16:40:15 +03:00
a526fcbeee Merge branch 'dev' into SP-1660-fe-ui-enhancement-update-confirmation-dialog-on-create-visitor-password-flow 2025-06-29 16:10:43 +03:00
172e1d208a [FE] Preferences & Calibration (#312)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1707](https://syncrow.atlassian.net/browse/SP-1707)

## Description

fix UI  like in figma

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1707]:
https://syncrow.atlassian.net/browse/SP-1707?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 16:10:00 +03:00
2c254c1a91 Sp 1771 fe device name and subspace changes not reflected immediately after update on device management page (#313)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1771](https://syncrow.atlassian.net/browse/SP-1771)

## Description

Synced state between settings and devices table.

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1771]:
https://syncrow.atlassian.net/browse/SP-1771?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 16:09:37 +03:00
480e183b91 Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1771-FE-Device-name-and-subspace-changes-not-reflected-immediately-after-update-on-Device-Management-page 2025-06-29 16:02:24 +03:00
d8bb234537 SP-1771 2025-06-29 16:00:15 +03:00
8916efcebb fixed aqi filter bugs. 2025-06-29 15:39:30 +03:00
175d1e662b Revert "Sp 1589 fe when user navigates to devices page the devices ar… (#311)
…e already listed although no community is selected also when we select
a community the api is being called repeatedly too many times (#305)"

This reverts commit 034a5ef908, reversing
changes made to b97183fb61.

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1589](https://syncrow.atlassian.net/browse/SP-1589)

## Description

revert sp:1589 cuz have problems in routin

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1589]:
https://syncrow.atlassian.net/browse/SP-1589?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 15:26:29 +03:00
57bd4b8527 Revert "Sp 1589 fe when user navigates to devices page the devices are already listed although no community is selected also when we select a community the api is being called repeatedly too many times (#305)"
This reverts commit 034a5ef908, reversing
changes made to b97183fb61.
2025-06-29 14:45:54 +03:00
df308fd12a Merge branch 'dev' of https://github.com/SyncrowIOT/web into SP-1771-FE-Device-name-and-subspace-changes-not-reflected-immediately-after-update-on-Device-Management-page 2025-06-29 14:14:00 +03:00
e0cfe541dd name changes in table when changed. 2025-06-29 14:13:25 +03:00
814cbf787f edit the UI as wanted in ticket (note: in figma is not updated yet to the requested ticket) 2025-06-29 13:58:57 +03:00
df8eff895e [FE] Create Scheduling UI (#309)
and funtion name in dialog was olways keep close now it is really take
the real value

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1705](https://syncrow.atlassian.net/browse/SP-1705)

## Description
in curtain Module
fix edit issue and insure function name in  table 

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1705]:
https://syncrow.atlassian.net/browse/SP-1705?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 13:14:08 +03:00
9514200892 sp:1728 [FE] Build Curtain Dialog Component (#307)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1728](https://syncrow.atlassian.net/browse/SP-1728)

## Description

change the name  to curtain functions and conditions

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1728]:
https://syncrow.atlassian.net/browse/SP-1728?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 13:13:47 +03:00
cf4bfc41f6 [FE] Disable AC Control Button When AC is Off (#308)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1387](https://syncrow.atlassian.net/browse/SP-1387)

## Description

fix bug to dont stack snackbars

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1387]:
https://syncrow.atlassian.net/browse/SP-1387?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-06-29 13:11:36 +03:00
01f55c14de Add update events for device and subspace names
implement copyWith methods in models
2025-06-29 13:03:33 +03:00
19cdd371f8 fix edit problem
and funtion name in dialog was olways keep close now it is really take the real value
2025-06-29 12:43:23 +03:00
388391eec4 stop stacking snackbars 2025-06-29 11:29:43 +03:00
23cfee1490 fix curtain name in curtain if then containers dialogs 2025-06-29 11:12:28 +03:00
2f5ad03431 created empty charts widget. 2025-06-29 10:57:18 +03:00
df34ded153 Add responsive input fields and radio groups for visitor password setup 2025-06-24 11:35:03 +03:00
38 changed files with 825 additions and 298 deletions

View File

@ -0,0 +1,8 @@
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
<rect x="16.0922" y="66.3794" width="28.1609" height="66.3793" fill="#C7CDD1"/>
<rect x="54.3105" y="24.1379" width="28.1609" height="108.621" fill="#ABB4BA"/>
<rect x="92.5288" y="78.4484" width="28.1609" height="54.3103" fill="#C7CDD1"/>
<rect x="130.747" y="48.2759" width="28.1609" height="84.4828" fill="#ABB4BA"/>
</svg>

After

Width:  |  Height:  |  Size: 583 B

View File

@ -0,0 +1,5 @@
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0.5" y1="3.05394e-05" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
<path d="M1.5 132.5C13 132.5 6.58852 66.5 29.5 66.5C46.5 66.5 46.1214 24.9349 68.5 24.5C94.2816 23.999 80.7136 78.5065 106.5 78.5C125.715 78.4952 131.5 48.5 145.5 48.5C159.5 48.5 156.5 96 171.5 96" stroke="#ABB4BA" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

View File

@ -0,0 +1,7 @@
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
<path d="M1.5 132.5C13 132.5 6.58852 66.5 29.5 66.5C46.5 66.5 46.1214 24.9348 68.5 24.5C94.2816 23.999 80.7136 78.5064 106.5 78.5C125.715 78.4951 131.5 48.5 145.5 48.5C159.5 48.5 156.5 95.9999 171.5 95.9999" stroke="#ABB4BA" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
<path d="M1.5 132.5C13 132.5 6.58852 78.4999 29.5 78.4999C46.5 78.4999 45.6214 44.9349 68 44.5C93.7816 43.999 80.7136 27.0065 106.5 27C125.715 26.9952 131.5 63.5 145.5 63.5C159.5 63.5 156.5 113.5 171.5 113.5" stroke="#C7CDD1" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
<path d="M1.5 132.5C13 132.5 6.58852 85.9999 29.5 85.9999C46.5 85.9999 45.6214 11.9348 68 11.4999C93.7816 10.9989 80.7136 43.5064 106.5 43.4999C125.715 43.4951 131.5 35.4999 145.5 35.4999C159.5 35.4999 156.5 105.5 171.5 105.5" stroke="#D5D5D5" stroke-width="1.5" stroke-linecap="round" stroke-dasharray="4 4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,99 @@
<svg width="181" height="121" viewBox="0 0 181 121" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="15.5" y1="-2.52181e-08" x2="15.5" y2="120" stroke="#B9B9B9"/>
<line x1="45.5" y1="-2.52181e-08" x2="45.5" y2="120" stroke="#B9B9B9"/>
<line x1="75.5" y1="-2.52181e-08" x2="75.5" y2="120" stroke="#B9B9B9"/>
<line x1="105.5" y1="-2.52181e-08" x2="105.5" y2="120" stroke="#B9B9B9"/>
<line x1="135.5" y1="-2.52181e-08" x2="135.5" y2="120" stroke="#B9B9B9"/>
<line x1="165.5" y1="-2.52181e-08" x2="165.5" y2="120" stroke="#B9B9B9"/>
<line x1="30.5" y1="-2.52181e-08" x2="30.5" y2="120" stroke="#B9B9B9"/>
<line x1="60.5" y1="-2.52181e-08" x2="60.5" y2="120" stroke="#B9B9B9"/>
<line x1="90.5" y1="-2.52181e-08" x2="90.5" y2="120" stroke="#B9B9B9"/>
<line x1="120.5" y1="-2.52181e-08" x2="120.5" y2="120" stroke="#B9B9B9"/>
<line x1="150.5" y1="-2.52181e-08" x2="150.5" y2="120" stroke="#B9B9B9"/>
<line x1="180.5" y1="-2.52181e-08" x2="180.5" y2="120" stroke="#B9B9B9"/>
<line x1="181" y1="120.5" x2="-4.52101e-08" y2="120.5" stroke="#B9B9B9"/>
<line x1="181" y1="60.5" x2="-4.52101e-08" y2="60.5" stroke="#B9B9B9"/>
<line x1="181" y1="90.5" x2="-4.52101e-08" y2="90.5" stroke="#B9B9B9"/>
<line x1="181" y1="30.5" x2="-4.52101e-08" y2="30.5" stroke="#B9B9B9"/>
<line x1="181" y1="105.5" x2="-4.52101e-08" y2="105.5" stroke="#B9B9B9"/>
<line x1="181" y1="45.5" x2="-4.52101e-08" y2="45.5" stroke="#B9B9B9"/>
<line x1="181" y1="75.5" x2="-4.52101e-08" y2="75.5" stroke="#B9B9B9"/>
<line x1="181" y1="15.5" x2="-4.52101e-08" y2="15.5" stroke="#B9B9B9"/>
<rect x="16" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="46" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="61" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="76" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="91" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="106" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="121" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="136" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="151" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="166" y="16" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="31" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="46" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="61" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="76" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="91" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="106" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="121" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="136" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="151" y="31" width="14" height="14" fill="#D5D5D5"/>
<rect x="166" y="31" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="46" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="46" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="61" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="76" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="91" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="106" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="121" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="136" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="151" y="46" width="14" height="14" fill="#D5D5D5"/>
<rect x="166" y="46" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="61" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="61" width="14" height="14" fill="#D5D5D5"/>
<rect x="46" y="61" width="14" height="14" fill="#D5D5D5"/>
<rect x="61" y="61" width="14" height="14" fill="#B1B1B1"/>
<rect x="76" y="61" width="14" height="14" fill="#B1B1B1"/>
<rect x="91" y="61" width="14" height="14" fill="#B1B1B1"/>
<rect x="106" y="61" width="14" height="14" fill="#B1B1B1"/>
<rect x="121" y="61" width="14" height="14" fill="#B1B1B1"/>
<rect x="136" y="61" width="14" height="14" fill="#D5D5D5"/>
<rect x="151" y="61" width="14" height="14" fill="#D5D5D5"/>
<rect x="166" y="61" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="76" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="46" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="61" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="76" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="91" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="106" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="121" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="136" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="151" y="76" width="14" height="14" fill="#D5D5D5"/>
<rect x="166" y="76" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="91" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="46" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="61" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="76" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="91" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="106" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="121" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="136" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="151" y="91" width="14" height="14" fill="#D5D5D5"/>
<rect x="166" y="91" width="14" height="14" fill="#F2F2F2"/>
<rect x="16" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="31" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="46" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="61" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="76" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="91" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="106" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="121" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="136" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="151" y="106" width="14" height="14" fill="#F2F2F2"/>
<rect x="166" y="106" width="14" height="14" fill="#F2F2F2"/>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,7 @@
<svg width="175" height="134" viewBox="0 0 175 134" fill="none" xmlns="http://www.w3.org/2000/svg">
<line x1="0.5" y1="2.18557e-08" x2="0.499994" y2="132.759" stroke="#B9C0C5"/>
<line x1="175" y1="133.259" x2="-4.37114e-08" y2="133.259" stroke="#B9C0C5"/>
<path d="M1.5 95.9999C13 95.9999 6.58853 66.4999 29.5 66.4999C46.5 66.4999 46.1214 34.9348 68.5 34.4999C94.2816 33.9989 80.7136 65.0065 106.5 65C125.715 64.9952 131.5 50.5 145.5 50.5C159.5 50.5 156.5 70.5 171.5 70.5" stroke="#C7CDD1" stroke-width="2" stroke-linecap="round"/>
<path d="M1.5 106C13 106 6.58853 76.4999 29.5 76.4999C46.5 76.4999 46.1214 44.9348 68.5 44.4999C94.2816 43.9989 80.7136 75.0065 106.5 75C125.715 74.9952 131.5 60.5 145.5 60.5C159.5 60.5 156.5 80.5 171.5 80.5" stroke="#F2F2F2" stroke-width="2" stroke-linecap="round"/>
<path d="M1.5 116C13 116 6.58853 86.4999 29.5 86.4999C46.5 86.4999 46.1214 54.9348 68.5 54.4999C94.2816 53.9989 80.7136 85.0065 106.5 85C125.715 84.9952 131.5 70.5 145.5 70.5C159.5 70.5 156.5 90.5 171.5 90.5" stroke="#ABB4BA" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -39,8 +39,12 @@ class AnalyticsDevice {
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
: null,
spaceUuid: json['spaceUuid'] as String?,
latitude: json['lat'] != null ? double.parse(json['lat'] as String? ?? '0.0') : null,
longitude: json['lon'] != null ? double.parse(json['lon'] as String? ?? '0.0') : null,
latitude: json['lat'] != null && json['lat'] != ''
? double.tryParse(json['lat']?.toString() ?? '0.0')
: null,
longitude: json['lon'] != null && json['lon'] != ''
? double.tryParse(json['lon']?.toString() ?? '0.0')
: null,
);
}
}

View File

@ -46,11 +46,11 @@ class AirQualityDistributionBloc
}
}
Future<void> _onClearAirQualityDistribution(
void _onClearAirQualityDistribution(
ClearAirQualityDistribution event,
Emitter<AirQualityDistributionState> emit,
) async {
emit(const AirQualityDistributionState());
) {
emit(AirQualityDistributionState(selectedAqiType: state.selectedAqiType));
}
void _onUpdateAqiTypeEvent(

View File

@ -75,6 +75,6 @@ class RangeOfAqiBloc extends Bloc<RangeOfAqiEvent, RangeOfAqiState> {
ClearRangeOfAqiEvent event,
Emitter<RangeOfAqiState> emit,
) {
emit(const RangeOfAqiState());
emit(RangeOfAqiState(selectedAqiType: state.selectedAqiType));
}
}

View File

@ -3,7 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/air_quality_distribution/air_quality_distribution_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class AqiDistributionChartBox extends StatelessWidget {
@ -32,8 +34,20 @@ class AqiDistributionChartBox extends StatelessWidget {
const SizedBox(height: 10),
const Divider(),
const SizedBox(height: 20),
Expanded(
child: AqiDistributionChart(chartData: state.chartData),
Visibility(
visible: state.chartData.isNotEmpty,
replacement: AnalyticsChartEmptyStateWidget(
isLoading: state.status == AirQualityDistributionStatus.loading,
isError: state.status == AirQualityDistributionStatus.failure,
isInitial: state.status == AirQualityDistributionStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyBarredChart,
),
child: Expanded(
child: AqiDistributionChart(
chartData: state.chartData,
),
),
),
],
),

View File

@ -34,6 +34,7 @@ class AqiDistributionChartTitle extends StatelessWidget {
alignment: AlignmentDirectional.centerEnd,
fit: BoxFit.scaleDown,
child: AqiTypeDropdown(
selectedAqiType: context.watch<AirQualityDistributionBloc>().state.selectedAqiType,
onChanged: (value) {
if (value != null) {
final bloc = context.read<AirQualityDistributionBloc>();

View File

@ -18,19 +18,20 @@ enum AqiType {
}
class AqiTypeDropdown extends StatefulWidget {
const AqiTypeDropdown({super.key, required this.onChanged});
const AqiTypeDropdown({
required this.onChanged,
this.selectedAqiType,
super.key,
});
final ValueChanged<AqiType?> onChanged;
final AqiType? selectedAqiType;
@override
State<AqiTypeDropdown> createState() => _AqiTypeDropdownState();
}
class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
AqiType? _selectedItem = AqiType.aqi;
void _updateSelectedItem(AqiType? item) => setState(() => _selectedItem = item);
@override
Widget build(BuildContext context) {
return Container(
@ -41,8 +42,8 @@ class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
width: 1,
),
),
child: DropdownButton<AqiType?>(
value: _selectedItem,
child: DropdownButton<AqiType>(
value: widget.selectedAqiType,
isDense: true,
borderRadius: BorderRadius.circular(16),
dropdownColor: ColorsManager.whiteColors,
@ -59,10 +60,7 @@ class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
items: AqiType.values
.map((e) => DropdownMenuItem(value: e, child: Text(e.value)))
.toList(),
onChanged: (value) {
_updateSelectedItem(value);
widget.onChanged(value);
},
onChanged: widget.onChanged,
),
);
}

View File

@ -3,7 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/blocs/range_of_aqi/range_of_aqi_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_title.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class RangeOfAqiChartBox extends StatelessWidget {
@ -32,10 +34,20 @@ class RangeOfAqiChartBox extends StatelessWidget {
const SizedBox(height: 10),
const Divider(),
const SizedBox(height: 20),
Expanded(
child: RangeOfAqiChart(
chartData: state.filteredRangeOfAqi,
selectedAqiType: state.selectedAqiType,
Visibility(
visible: state.filteredRangeOfAqi.isNotEmpty,
replacement: AnalyticsChartEmptyStateWidget(
isLoading: state.status == RangeOfAqiStatus.loading,
isError: state.status == RangeOfAqiStatus.failure,
isInitial: state.status == RangeOfAqiStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyRangeOfAqi,
),
child: Expanded(
child: RangeOfAqiChart(
chartData: state.filteredRangeOfAqi,
selectedAqiType: state.selectedAqiType,
),
),
),
],

View File

@ -63,15 +63,15 @@ class RangeOfAqiChartTitle extends StatelessWidget {
fit: BoxFit.scaleDown,
alignment: AlignmentDirectional.centerEnd,
child: AqiTypeDropdown(
selectedAqiType: context.watch<RangeOfAqiBloc>().state.selectedAqiType,
onChanged: (value) {
final spaceTreeState = context.read<SpaceTreeBloc>().state;
final spaceUuid = spaceTreeState.selectedSpaces.firstOrNull;
if (spaceUuid == null) return;
if (value != null) {
context.read<RangeOfAqiBloc>().add(UpdateAqiTypeEvent(value));
}
if (spaceUuid == null) return;
},
),
),

View File

@ -5,8 +5,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/ener
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_chart.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/energy_consumption_per_device_devices_list.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/charts_loading_widget.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
@ -54,8 +56,24 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
const SizedBox(height: 20),
const Divider(height: 0),
const SizedBox(height: 20),
Expanded(
child: EnergyConsumptionPerDeviceChart(chartData: state.chartData),
Visibility(
visible: state.chartData.isNotEmpty &&
state.chartData
.every((e) => e.energy.every((e) => e.value != 0)),
replacement: AnalyticsChartEmptyStateWidget(
isLoading:
state.status == EnergyConsumptionPerDeviceStatus.loading,
isError: state.status == EnergyConsumptionPerDeviceStatus.failure,
isInitial:
state.status == EnergyConsumptionPerDeviceStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyEnergyManagementPerDevice,
),
child: Expanded(
child: EnergyConsumptionPerDeviceChart(
chartData: state.chartData,
),
),
),
],
),

View File

@ -3,8 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/total_energy_consumption/total_energy_consumption_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/chart_title.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/total_energy_consumption_chart.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/charts_loading_widget.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class TotalEnergyConsumptionChartBox extends StatelessWidget {
@ -41,7 +43,18 @@ class TotalEnergyConsumptionChartBox extends StatelessWidget {
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
TotalEnergyConsumptionChart(chartData: state.chartData),
Visibility(
visible: state.chartData.isNotEmpty &&
state.chartData.every((e) => e.value != 0),
replacement: AnalyticsChartEmptyStateWidget(
isLoading: state.status == TotalEnergyConsumptionStatus.loading,
isError: state.status == TotalEnergyConsumptionStatus.failure,
isInitial: state.status == TotalEnergyConsumptionStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyEnergyManagementChart,
),
child: TotalEnergyConsumptionChart(chartData: state.chartData),
),
],
),
),

View File

@ -6,8 +6,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/ch
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy/occupancy_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_chart.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class OccupancyChartBox extends StatelessWidget {
@ -67,7 +69,24 @@ class OccupancyChartBox extends StatelessWidget {
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
Expanded(child: OccupancyChart(chartData: state.chartData)),
Visibility(
visible: state.chartData.isNotEmpty &&
state.chartData.every(
(e) => e.occupancy.isNotEmpty,
),
replacement: AnalyticsChartEmptyStateWidget(
isLoading: state.status == OccupancyStatus.loading,
isError: state.status == OccupancyStatus.failure,
isInitial: state.status == OccupancyStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyBarredChart,
),
child: Expanded(
child: OccupancyChart(
chartData: state.chartData,
),
),
),
],
),
);

View File

@ -6,8 +6,10 @@ import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/ch
import 'package:syncrow_web/pages/analytics/modules/occupancy/blocs/occupancy_heat_map/occupancy_heat_map_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/occupancy/helpers/fetch_occupancy_data_helper.dart';
import 'package:syncrow_web/pages/analytics/modules/occupancy/widgets/occupancy_heat_map.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_chart_empty_state_widget.dart';
import 'package:syncrow_web/pages/analytics/widgets/analytics_error_widget.dart';
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
import 'package:syncrow_web/utils/style.dart';
class OccupancyHeatMapBox extends StatelessWidget {
@ -68,16 +70,29 @@ class OccupancyHeatMapBox extends StatelessWidget {
const SizedBox(height: 20),
const Divider(),
const SizedBox(height: 20),
Expanded(
child: OccupancyHeatMap(
selectedDate:
context.watch<AnalyticsDatePickerBloc>().state.yearlyDate,
heatMapData: state.heatMapData.asMap().map(
(_, value) => MapEntry(
value.eventDate,
value.countTotalPresenceDetected,
Visibility(
visible: state.heatMapData.isNotEmpty &&
state.heatMapData.every(
(e) => e.countTotalPresenceDetected != 0,
),
replacement: AnalyticsChartEmptyStateWidget(
isLoading: state.status == OccupancyHeatMapStatus.loading,
isError: state.status == OccupancyHeatMapStatus.failure,
isInitial: state.status == OccupancyHeatMapStatus.initial,
errorMessage: state.errorMessage,
iconPath: Assets.emptyHeatmap,
),
child: Expanded(
child: OccupancyHeatMap(
selectedDate:
context.watch<AnalyticsDatePickerBloc>().state.yearlyDate,
heatMapData: state.heatMapData.asMap().map(
(_, value) => MapEntry(
value.eventDate,
value.countTotalPresenceDetected,
),
),
),
),
),
),
],

View File

@ -0,0 +1,68 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/common/widgets/app_loading_indicator.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class AnalyticsChartEmptyStateWidget extends StatelessWidget {
const AnalyticsChartEmptyStateWidget({
required this.iconPath,
this.isLoading = false,
this.isError = false,
this.isInitial = false,
this.errorMessage,
this.noDataMessage = 'No data to display',
this.initialMessage = 'Please select a space to see data',
super.key,
});
final bool isLoading;
final bool isError;
final bool isInitial;
final String? errorMessage;
final String noDataMessage;
final String initialMessage;
final String iconPath;
@override
Widget build(BuildContext context) {
return Expanded(
child: _buildWidgetBasedOnState(context),
);
}
Widget _buildWidgetBasedOnState(BuildContext context) {
final widgetsMap = {
isLoading: const AppLoadingIndicator(),
isInitial: _buildState(context, initialMessage),
isError: _buildState(context, errorMessage ?? 'Something went wrong'),
};
return widgetsMap[true] ?? _buildState(context, noDataMessage);
}
Widget _buildState(BuildContext context, String message) {
return Center(
child: Column(
spacing: 16,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 16),
Expanded(child: SvgPicture.asset(iconPath, fit: BoxFit.contain)),
SelectableText(
message,
style: isError
? context.textTheme.bodyMedium?.copyWith(
color: ColorsManager.red,
fontSize: 16,
fontWeight: FontWeight.w700,
)
: null,
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
],
),
);
}
}

View File

@ -16,11 +16,12 @@ class DeviceManagementBloc
int _onlineCount = 0;
int _offlineCount = 0;
int _lowBatteryCount = 0;
List<AllDevicesModel> _selectedDevices = [];
final List<AllDevicesModel> _selectedDevices = [];
List<AllDevicesModel> _filteredDevices = [];
String currentProductName = '';
String? currentCommunity;
String? currentUnitName;
String subSpaceName = '';
DeviceManagementBloc() : super(DeviceManagementInitial()) {
on<FetchDevices>(_onFetchDevices);
@ -31,24 +32,29 @@ class DeviceManagementBloc
on<ResetFilters>(_onResetFilters);
on<ResetSelectedDevices>(_onResetSelectedDevices);
on<UpdateSelection>(_onUpdateSelection);
on<UpdateDeviceName>(_onUpdateDeviceName);
on<UpdateSubSpaceName>(_onUpdateSubSpaceName);
}
Future<void> _onFetchDevices(
FetchDevices event, Emitter<DeviceManagementState> emit) async {
emit(DeviceManagementLoading());
try {
List<AllDevicesModel> devices = [];
var devices = <AllDevicesModel>[];
_devices.clear();
var spaceBloc = event.context.read<SpaceTreeBloc>();
final spaceBloc = event.context.read<SpaceTreeBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = await DevicesManagementApi().fetchDevices(projectUuid);
devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
} else {
for (var community in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
for (final community in spaceBloc.state.selectedCommunities) {
final spacesList =
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
for (final space in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(community, space, projectUuid));
}
}
}
@ -70,7 +76,7 @@ class DeviceManagementBloc
}
}
void _onFilterDevices(
Future<void> _onFilterDevices(
FilterDevices event, Emitter<DeviceManagementState> emit) async {
if (_devices.isNotEmpty) {
_filteredDevices = List.from(_devices.where((device) {
@ -152,8 +158,7 @@ class DeviceManagementBloc
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
}
void _onSelectDevice(
SelectDevice event, Emitter<DeviceManagementState> emit) {
void _onSelectDevice(SelectDevice event, Emitter<DeviceManagementState> emit) {
final selectedUuid = event.selectedDevice.uuid;
if (_selectedDevices.any((device) => device.uuid == selectedUuid)) {
@ -162,9 +167,9 @@ class DeviceManagementBloc
_selectedDevices.add(event.selectedDevice);
}
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices);
final clonedSelectedDevices = List<AllDevicesModel>.from(_selectedDevices);
bool isControlButtonEnabled =
final isControlButtonEnabled =
_checkIfControlButtonEnabled(clonedSelectedDevices);
if (state is DeviceManagementLoaded) {
@ -194,8 +199,8 @@ class DeviceManagementBloc
void _onUpdateSelection(
UpdateSelection event, Emitter<DeviceManagementState> emit) {
List<AllDevicesModel> selectedDevices = [];
List<AllDevicesModel> devicesToSelectFrom = [];
final selectedDevices = <AllDevicesModel>[];
var devicesToSelectFrom = <AllDevicesModel>[];
if (state is DeviceManagementLoaded) {
devicesToSelectFrom = (state as DeviceManagementLoaded).devices;
@ -203,7 +208,7 @@ class DeviceManagementBloc
devicesToSelectFrom = (state as DeviceManagementFiltered).filteredDevices;
}
for (int i = 0; i < event.selectedRows.length; i++) {
for (var i = 0; i < event.selectedRows.length; i++) {
if (event.selectedRows[i]) {
selectedDevices.add(devicesToSelectFrom[i]);
}
@ -249,8 +254,7 @@ class DeviceManagementBloc
_onlineCount = _devices.where((device) => device.online == true).length;
_offlineCount = _devices.where((device) => device.online == false).length;
_lowBatteryCount = _devices
.where((device) =>
device.batteryLevel != null && device.batteryLevel! < 20)
.where((device) => device.batteryLevel != null && device.batteryLevel! < 20)
.length;
}
@ -267,8 +271,7 @@ class DeviceManagementBloc
}
}
void _onSearchDevices(
SearchDevices event, Emitter<DeviceManagementState> emit) {
void _onSearchDevices(SearchDevices event, Emitter<DeviceManagementState> emit) {
if ((event.community == null || event.community!.isEmpty) &&
(event.unitName == null || event.unitName!.isEmpty) &&
(event.deviceNameOrProductName == null ||
@ -297,7 +300,7 @@ class DeviceManagementBloc
currentCommunity = event.community;
currentUnitName = event.unitName;
List<AllDevicesModel> devicesToSearch = _devices;
final devicesToSearch = _devices;
if (devicesToSearch.isNotEmpty) {
final searchText = event.deviceNameOrProductName?.toLowerCase() ?? '';
@ -340,5 +343,134 @@ class DeviceManagementBloc
}
}
void _onUpdateDeviceName(
UpdateDeviceName event, Emitter<DeviceManagementState> emit) {
final devices = _devices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
_selectedDevices.removeWhere((device) => device.uuid == event.deviceId);
_selectedDevices.add(modifiedDevice);
return modifiedDevice;
}
return device;
}).toList();
final filteredDevices = _filteredDevices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
_selectedDevices.removeWhere((device) => device.uuid == event.deviceId);
_selectedDevices.add(modifiedDevice);
return modifiedDevice;
}
return device;
}).toList();
_devices = devices;
_filteredDevices = filteredDevices;
if (state is DeviceManagementLoaded) {
final loaded = state as DeviceManagementLoaded;
final selectedDevices01 = _selectedDevices.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
return modifiedDevice;
}
return device;
}).toList();
emit(DeviceManagementLoaded(
devices: devices,
selectedIndex: loaded.selectedIndex,
onlineCount: loaded.onlineCount,
offlineCount: loaded.offlineCount,
lowBatteryCount: loaded.lowBatteryCount,
selectedDevice: selectedDevices01,
isControlButtonEnabled: loaded.isControlButtonEnabled,
));
} else if (state is DeviceManagementFiltered) {
final filtered = state as DeviceManagementFiltered;
final selectedDevices01 = filtered.selectedDevice?.map((device) {
if (device.uuid == event.deviceId) {
final modifiedDevice = device.copyWith(name: event.newName);
return modifiedDevice;
}
return device;
}).toList();
emit(DeviceManagementFiltered(
filteredDevices: filteredDevices,
selectedIndex: filtered.selectedIndex,
onlineCount: filtered.onlineCount,
offlineCount: filtered.offlineCount,
lowBatteryCount: filtered.lowBatteryCount,
selectedDevice: selectedDevices01,
isControlButtonEnabled: filtered.isControlButtonEnabled,
));
}
}
void _onUpdateSubSpaceName(
UpdateSubSpaceName event, Emitter<DeviceManagementState> emit) {
final devices = _devices.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
final filteredDevices = _filteredDevices.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
_devices = devices;
_filteredDevices = filteredDevices;
if (state is DeviceManagementLoaded) {
final loaded = state as DeviceManagementLoaded;
final selectedDevices = loaded.selectedDevice?.map((device) {
if (device.uuid == event.deviceId) {
return device.copyWith(
subspace:
device.subspace?.copyWith(subspaceName: event.newSubSpaceName));
}
return device;
}).toList();
emit(DeviceManagementLoaded(
devices: _devices,
selectedIndex: loaded.selectedIndex,
onlineCount: loaded.onlineCount,
offlineCount: loaded.offlineCount,
lowBatteryCount: loaded.lowBatteryCount,
selectedDevice: selectedDevices,
isControlButtonEnabled: loaded.isControlButtonEnabled,
));
} else if (state is DeviceManagementFiltered) {
// final filtered = state as DeviceManagementFiltered;
// emit(DeviceManagementFiltered(
// filteredDevices: _filteredDevices,
// selectedIndex: filtered.selectedIndex,
// onlineCount: filtered.onlineCount,
// offlineCount: filtered.offlineCount,
// lowBatteryCount: filtered.lowBatteryCount,
// selectedDevice: filtered.selectedDevice,
// isControlButtonEnabled: filtered.isControlButtonEnabled,
// ));
}
}
void changeSubspaceName(
String deviceId, String newSubSpaceName, String subspaceId) {
add(UpdateSubSpaceName(
deviceId: deviceId,
newSubSpaceName: newSubSpaceName,
subspaceId: subspaceId,
));
}
List<AllDevicesModel> get selectedDevices => _selectedDevices;
}

View File

@ -70,3 +70,21 @@ class UpdateSelection extends DeviceManagementEvent {
const UpdateSelection(this.selectedRows);
}
class UpdateDeviceName extends DeviceManagementEvent {
final String deviceId;
final String newName;
const UpdateDeviceName({required this.deviceId, required this.newName});
}
class UpdateSubSpaceName extends DeviceManagementEvent {
final String deviceId;
final String newSubSpaceName;
final String subspaceId;
const UpdateSubSpaceName(
{required this.deviceId,
required this.newSubSpaceName,
required this.subspaceId});
}

View File

@ -57,6 +57,16 @@ class Status {
};
}
Status copyWith({
String? code,
dynamic value,
}) {
return Status(
code: code ?? this.code,
value: value ?? this.value,
);
}
factory Status.fromJson(String source) => Status.fromMap(json.decode(source));
String toJson() => json.encode(toMap());

View File

@ -44,4 +44,20 @@ class DeviceSubspace {
static List<Map<String, dynamic>> listToJson(List<DeviceSubspace> subspaces) {
return subspaces.map((subspace) => subspace.toJson()).toList();
}
DeviceSubspace copyWith({
String? uuid,
DateTime? createdAt,
DateTime? updatedAt,
String? subspaceName,
bool? disabled,
}) {
return DeviceSubspace(
uuid: uuid ?? this.uuid,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
subspaceName: subspaceName ?? this.subspaceName,
disabled: disabled ?? this.disabled,
);
}
}

View File

@ -588,4 +588,72 @@ SOS
"NCPS": DeviceType.NCPS,
"PC": DeviceType.PC,
};
AllDevicesModel copyWith({
DevicesModelRoom? room,
DeviceSubspace? subspace,
DevicesModelUnit? unit,
DeviceCommunityModel? community,
String? productUuid,
String? productType,
String? permissionType,
int? activeTime,
String? category,
String? categoryName,
int? createTime,
String? gatewayId,
String? icon,
String? ip,
String? lat,
String? localKey,
String? lon,
String? model,
String? name,
String? nodeId,
bool? online,
String? ownerId,
bool? sub,
String? timeZone,
int? updateTime,
String? uuid,
int? batteryLevel,
String? productName,
List<DeviceSpaceModel>? spaces,
List<DeviceTagModel>? deviceTags,
DeviceSubSpace? deviceSubSpace,
}) {
return AllDevicesModel(
room: room ?? this.room,
subspace: subspace ?? this.subspace,
unit: unit ?? this.unit,
community: community ?? this.community,
productUuid: productUuid ?? this.productUuid,
productType: productType ?? this.productType,
permissionType: permissionType ?? this.permissionType,
activeTime: activeTime ?? this.activeTime,
category: category ?? this.category,
categoryName: categoryName ?? this.categoryName,
createTime: createTime ?? this.createTime,
gatewayId: gatewayId ?? this.gatewayId,
icon: icon ?? this.icon,
ip: ip ?? this.ip,
lat: lat ?? this.lat,
localKey: localKey ?? this.localKey,
lon: lon ?? this.lon,
model: model ?? this.model,
name: name ?? this.name,
nodeId: nodeId ?? this.nodeId,
online: online ?? this.online,
ownerId: ownerId ?? this.ownerId,
sub: sub ?? this.sub,
timeZone: timeZone ?? this.timeZone,
updateTime: updateTime ?? this.updateTime,
uuid: uuid ?? this.uuid,
batteryLevel: batteryLevel ?? this.batteryLevel,
productName: productName ?? this.productName,
spaces: spaces ?? this.spaces,
deviceTags: deviceTags ?? this.deviceTags,
deviceSubSpace: deviceSubSpace ?? this.deviceSubSpace,
);
}
}

View File

@ -23,6 +23,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
@override
Widget build(BuildContext context) {
return BlocBuilder<DeviceManagementBloc, DeviceManagementState>(
buildWhen: (previous, current) => previous != current,
builder: (context, state) {
List<AllDevicesModel> devicesToShow = [];
int selectedIndex = 0;
@ -31,7 +32,6 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
int lowBatteryCount = 0;
bool isControlButtonEnabled = false;
List<AllDevicesModel> selectedDevices = [];
if (state is DeviceManagementLoaded) {
devicesToShow = state.devices;
selectedIndex = state.selectedIndex;
@ -111,6 +111,8 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
onPressed: isControlButtonEnabled
? () {
if (isAnyDeviceOffline) {
ScaffoldMessenger.of(context)
.clearSnackBars();
ScaffoldMessenger.of(context)
.showSnackBar(
const SnackBar(
@ -190,7 +192,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
'Product Name',
'Device ID',
'Space Name',
'location',
'Location',
'Battery Level',
'Installation Date and Time',
'Status',
@ -242,7 +244,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
.map((device) => device.uuid!)
.toList(),
isEmpty: devicesToShow.isEmpty,
onSettingsPressed: (rowIndex) {
onSettingsPressed: (rowIndex) async {
final device = devicesToShow[rowIndex];
showDeviceSettingsSidebar(context, device);
},
@ -264,7 +266,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
barrierDismissible: true,
barrierLabel: "Device Settings",
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, anim1, anim2) {
pageBuilder: (_, anim1, anim2) {
return Align(
alignment: Alignment.centerRight,
child: Material(
@ -274,6 +276,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
child: DeviceSettingsPanel(
device: device,
onClose: () => Navigator.of(context).pop(),
deviceManagementBloc: context.read<DeviceManagementBloc>(),
),
),
),

View File

@ -17,7 +17,6 @@ class CalibrateCompletedDialog extends StatelessWidget {
@override
Widget build(_) {
return AlertDialog(
backgroundColor: ColorsManager.whiteColors,
contentPadding: EdgeInsets.zero,
content: SizedBox(
height: 250,

View File

@ -19,11 +19,14 @@ class DeviceManagementContent extends StatelessWidget {
required this.device,
required this.subSpaces,
required this.deviceInfo,
required this.deviceManagementBloc,
});
final AllDevicesModel device;
final List<SubSpaceModel> subSpaces;
final DeviceInfoModel deviceInfo;
final DeviceManagementBloc deviceManagementBloc;
@override
Widget build(BuildContext context) {
@ -87,6 +90,11 @@ class DeviceManagementContent extends StatelessWidget {
),
);
});
deviceManagementBloc.add(UpdateSubSpaceName(
subspaceId: selectedSubSpace.id!,
deviceId: device.uuid!,
newSubSpaceName: selectedSubSpace.name ?? ''));
}
},
child: infoRow(

View File

@ -1,13 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/device_icon_type_helper.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/device_management_content.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/remove_device_widget.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/device_info_model.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_bloc.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/bloc/setting_bloc_state.dart';
import 'package:syncrow_web/pages/device_managment/device_setting/settings_model/sub_space_model.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart';
@ -17,7 +19,13 @@ import 'package:syncrow_web/web_layout/default_container.dart';
class DeviceSettingsPanel extends StatelessWidget {
final VoidCallback? onClose;
final AllDevicesModel device;
const DeviceSettingsPanel({super.key, this.onClose, required this.device});
final DeviceManagementBloc deviceManagementBloc;
const DeviceSettingsPanel({
super.key,
this.onClose,
required this.device,
required this.deviceManagementBloc,
});
@override
Widget build(BuildContext context) {
@ -71,10 +79,10 @@ class DeviceSettingsPanel extends StatelessWidget {
'Device Settings',
style: context.theme.textTheme.titleLarge!
.copyWith(
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w700,
color: ColorsManager.vividBlue
.withOpacity(0.7),
fontSize: 24),
fontSize: 24),
),
],
),
@ -134,8 +142,14 @@ class DeviceSettingsPanel extends StatelessWidget {
onFieldSubmitted: (value) {
_bloc.add(const ChangeNameEvent(
value: false));
deviceManagementBloc
..add(UpdateDeviceName(
deviceId: device.uuid!,
newName: _bloc
.nameController
.text))..add(ResetSelectedDevices());
},
decoration: InputDecoration(
decoration:const InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
@ -157,7 +171,7 @@ class DeviceSettingsPanel extends StatelessWidget {
onTap: () {
_bloc.add(
const ChangeNameEvent(
value: true));
value: true));
},
child: SvgPicture.asset(
Assets
@ -190,6 +204,7 @@ class DeviceSettingsPanel extends StatelessWidget {
device: device,
subSpaces: subSpaces.cast<SubSpaceModel>(),
deviceInfo: deviceInfo,
deviceManagementBloc: deviceManagementBloc,
),
const SizedBox(height: 32),
RemoveDeviceWidget(bloc: _bloc),

View File

@ -286,11 +286,20 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
try {
if (state is ScheduleLoaded) {
final dateTime = DateTime.parse(event.time);
Status status = Status(code: '', value: '');
if (event.category == 'CUR_2') {
status = status.copyWith(
code: 'control',
value: event.functionOn == true ? 'open' : 'close');
} else {
status =
status.copyWith(code: event.category, value: event.functionOn);
}
final updatedSchedule = ScheduleEntry(
scheduleId: event.scheduleId,
category: event.category,
time: getTimeStampWithoutSeconds(dateTime).toString(),
function: Status(code: event.category, value: event.functionOn),
function: status,
days: event.selectedDays,
);
final success = await DevicesManagementApi().editScheduleRecord(

View File

@ -52,9 +52,12 @@ class BuildScheduleView extends StatelessWidget {
children: [
const ScheduleHeader(),
const SizedBox(height: 20),
ScheduleModeSelector(
currentMode: state.scheduleMode,
),
if (category == 'CUR_2')
const SizedBox()
else
ScheduleModeSelector(
currentMode: state.scheduleMode,
),
const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.schedule)
ScheduleManagementUI(

View File

@ -195,10 +195,10 @@ class _ScheduleTableView extends StatelessWidget {
child: Text(_getSelectedDays(
ScheduleModel.parseSelectedDays(schedule.days)))),
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
schedule.category == 'CUR_2'
? Center(
child: Text(schedule.function.value == true ? 'open' : 'close'))
: Center(child: Text(schedule.function.value ? 'On' : 'Off')),
if (schedule.category == 'CUR_2')
Center(child: Text(schedule.function.value))
else
Center(child: Text(schedule.function.value ? 'On' : 'Off')),
Center(
child: Wrap(
runAlignment: WrapAlignment.center,
@ -212,12 +212,20 @@ class _ScheduleTableView extends StatelessWidget {
isEdit: true,
).then((updatedSchedule) {
if (updatedSchedule != null) {
bool temp;
if (schedule.category == 'CUR_2') {
updatedSchedule.function.value == 'open'
? temp = true
: temp = false;
} else {
temp = updatedSchedule.function.value;
}
context.read<ScheduleBloc>().add(
ScheduleEditEvent(
scheduleId: schedule.scheduleId,
category: schedule.category,
time: updatedSchedule.time,
functionOn: updatedSchedule.function.value,
functionOn: temp,
selectedDays: updatedSchedule.days),
);
}

View File

@ -19,13 +19,19 @@ class ScheduleDialogHelper {
bool isEdit = false,
String? code,
}) {
bool temp;
if (schedule?.category == 'CUR_2') {
temp = schedule!.function.value == 'open' ? true : false;
} else {
temp = schedule!.function.value;
}
final initialTime = schedule != null
? _convertStringToTimeOfDay(schedule.time)
: TimeOfDay.now();
final initialDays = schedule != null
? _convertDaysStringToBooleans(schedule.days)
: List.filled(7, false);
bool? functionOn = schedule?.function.value ?? true;
bool? functionOn = temp;
TimeOfDay selectedTime = initialTime;
List<bool> selectedDays = List.of(initialDays);

View File

@ -170,45 +170,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
@ -936,12 +936,16 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
}
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(projectUuid,
spacesId: [createRoutineBloc.selectedSpaceId]));
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
}
emit(state.copyWith(isLoading: false, devices: devices));

View File

@ -58,7 +58,9 @@ class CurtainHelper {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const DialogHeader('AC Functions'),
DialogHeader(dialogType == 'THEN'
? 'Curtain Functions'
: 'Curtain Conditions'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,

View File

@ -58,11 +58,14 @@ class ProductModel {
'3G': Assets.Gang3SwitchIcon,
'3GT': Assets.threeTouchSwitch,
'CUR': Assets.curtain,
'CUR_2': Assets.curtain,
'GD': Assets.garageDoor,
'GW': Assets.SmartGatewayIcon,
'DL': Assets.DoorLockIcon,
'WL': Assets.waterLeakSensor,
'WH': Assets.waterHeater,
'WM': Assets.waterLeakSensor,
'SOS': Assets.sos,
'AC': Assets.ac,
'CPS': Assets.presenceSensor,
'PC': Assets.powerClamp,

View File

@ -2,10 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/common/date_time_widget.dart';
import 'package:syncrow_web/pages/common/text_field/custom_web_textfield.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_event.dart';
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_state.dart';
@ -23,8 +21,8 @@ class VisitorPasswordDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
var text = Theme.of(context)
final size = MediaQuery.of(context).size;
final text = Theme.of(context)
.textTheme
.bodySmall!
.copyWith(color: Colors.black, fontSize: 13);
@ -41,8 +39,7 @@ class VisitorPasswordDialog extends StatelessWidget {
title: 'Sent Successfully',
widgeta: Column(
children: [
if (visitorBloc
.passwordStatus!.failedOperations.isNotEmpty)
if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
Column(
children: [
const Text('Failed Devices'),
@ -56,22 +53,19 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.failedOperations.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc
.passwordStatus!
.failedOperations[index]
.deviceName)),
child: Text(visitorBloc.passwordStatus!
.failedOperations[index].deviceName)),
);
},
),
),
],
),
if (visitorBloc
.passwordStatus!.successOperations.isNotEmpty)
if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
Column(
children: [
const Text('Success Devices'),
@ -85,14 +79,12 @@ class VisitorPasswordDialog extends StatelessWidget {
.passwordStatus!.successOperations.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.all(5),
margin: const EdgeInsets.all(5),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc
.passwordStatus!
.successOperations[index]
.deviceName)),
child: Text(visitorBloc.passwordStatus!
.successOperations[index].deviceName)),
);
},
),
@ -115,16 +107,14 @@ class VisitorPasswordDialog extends StatelessWidget {
child: BlocBuilder<VisitorPasswordBloc, VisitorPasswordState>(
builder: (BuildContext context, VisitorPasswordState state) {
final visitorBloc = BlocProvider.of<VisitorPasswordBloc>(context);
bool isRepeat =
final isRepeat =
state is IsRepeatState ? state.repeat : visitorBloc.repeat;
return AlertDialog(
backgroundColor: Colors.white,
title: Text(
'Create visitor password',
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 24,
color: Colors.black),
fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black),
),
content: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
@ -310,14 +300,12 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context,
isEffective: false));
context: context, isEffective: false));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
}
},
startTime: () {
@ -326,31 +314,28 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context,
isEffective: true));
context: context, isEffective: true));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
}
},
firstString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess
.toString(),
firstString:
(visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess,
secondString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.expirationTime
: visitorBloc.endTimeAccess.toString(),
: visitorBloc.endTimeAccess,
icon: Assets.calendarIcon),
const SizedBox(
height: 10,
@ -410,8 +395,7 @@ class VisitorPasswordDialog extends StatelessWidget {
child: CupertinoSwitch(
value: visitorBloc.repeat,
onChanged: (value) {
visitorBloc
.add(ToggleRepeatEvent());
visitorBloc.add(ToggleRepeatEvent());
},
applyTheme: true,
),
@ -442,8 +426,7 @@ class VisitorPasswordDialog extends StatelessWidget {
},
).then((listDevice) {
if (listDevice != null) {
visitorBloc.selectedDevices =
listDevice;
visitorBloc.selectedDevices = listDevice;
}
});
},
@ -455,8 +438,7 @@ class VisitorPasswordDialog extends StatelessWidget {
.bodySmall!
.copyWith(
fontWeight: FontWeight.w400,
color:
ColorsManager.whiteColors,
color: ColorsManager.whiteColors,
fontSize: 12),
),
),
@ -495,37 +477,30 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () {
if (visitorBloc.forgetFormKey.currentState!.validate()) {
if (visitorBloc.selectedDevices.isNotEmpty) {
if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
setPasswordFunction(context, size, visitorBloc);
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time') {
setPasswordFunction(context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else if (visitorBloc.endTimeAccess.toString() !=
'End Time' &&
visitorBloc.startTimeAccess.toString() !=
'Start Time') {
} else if (visitorBloc.endTimeAccess != 'End Time' &&
visitorBloc.startTimeAccess != 'Start Time') {
if (visitorBloc.effectiveTimeTimeStamp != null &&
visitorBloc.expirationTimeTimeStamp != null) {
if (isRepeat == true) {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time' &&
visitorBloc.selectedDays.isNotEmpty) {
setPasswordFunction(
context, size, visitorBloc);
setPasswordFunction(context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
@ -539,15 +514,13 @@ class VisitorPasswordDialog extends StatelessWidget {
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else {
visitorBloc.stateDialog(
context: context,
message:
'Please select Access Period to continue',
message: 'Please select Access Period to continue',
title: 'Access Period');
}
} else {
@ -593,17 +566,17 @@ class VisitorPasswordDialog extends StatelessWidget {
alignment: Alignment.center,
content: SizedBox(
height: size.height * 0.25,
child: Center(
child:
CircularProgressIndicator(), // Display a loading spinner
child: const Center(
child: CircularProgressIndicator(), // Display a loading spinner
),
),
);
} else {
return AlertDialog(
alignment: Alignment.center,
backgroundColor: Colors.white,
content: SizedBox(
height: size.height * 0.25,
height: size.height * 0.13,
child: Column(
children: [
Column(
@ -617,13 +590,16 @@ class VisitorPasswordDialog extends StatelessWidget {
width: 35,
),
),
const SizedBox(
height: 20,
),
Text(
'Set Password',
style: Theme.of(context)
.textTheme
.headlineLarge!
.copyWith(
fontSize: 30,
fontSize: 24,
fontWeight: FontWeight.w400,
color: Colors.black,
),
@ -631,15 +607,6 @@ class VisitorPasswordDialog extends StatelessWidget {
],
),
const SizedBox(width: 15),
Text(
'This action will update all of the selected\n door locks passwords in the property.\n\nAre you sure you want to continue?',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
color: ColorsManager.grayColor,
fontWeight: FontWeight.w400,
fontSize: 18,
),
),
],
),
),
@ -668,12 +635,12 @@ class VisitorPasswordDialog extends StatelessWidget {
decoration: containerDecoration,
width: size.width * 0.1,
child: DefaultButton(
backgroundColor: Color(0xff023DFE),
borderRadius: 8,
onPressed: () {
Navigator.pop(context);
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add(OnlineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -681,8 +648,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.add(OnlineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,
@ -693,8 +659,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add(OfflineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -702,8 +667,7 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.add(OfflineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,
@ -715,7 +679,7 @@ class VisitorPasswordDialog extends StatelessWidget {
}
},
child: Text(
'Ok',
'Confirm',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,

View File

@ -13,13 +13,15 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
class DevicesManagementApi {
Future<List<AllDevicesModel>> fetchDevices(
String projectId, {
List<String>? spacesId,
}) async {
String communityId, String spaceId, String projectId) async {
try {
final response = await HTTPService().get(
queryParameters: {if (spacesId != null) 'spaces': spacesId},
path: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
path: communityId.isNotEmpty && spaceId.isNotEmpty
? ApiEndpoints.getSpaceDevices
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{communityUuid}', communityId)
.replaceAll('{projectId}', projectId)
: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json['data'];
@ -414,4 +416,5 @@ class DevicesManagementApi {
);
return response;
}
}

View File

@ -18,7 +18,7 @@ abstract class ApiEndpoints {
static const String getAllDevices = '/projects/{projectId}/devices';
static const String getSpaceDevices =
'/projects/{projectId}/devices';
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
static const String getDeviceStatus = '/devices/{uuid}/functions/status';
static const String getBatchStatus = '/devices/batch';

View File

@ -14,8 +14,7 @@ class Assets {
static const String rightLine = 'assets/images/right_line.png';
static const String google = 'assets/images/google.svg';
static const String facebook = 'assets/images/facebook.svg';
static const String invisiblePassword =
'assets/images/Password_invisible.svg';
static const String invisiblePassword = 'assets/images/Password_invisible.svg';
static const String visiblePassword = 'assets/images/password_visible.svg';
static const String accessIcon = 'assets/images/access_icon.svg';
static const String spaseManagementIcon =
@ -34,8 +33,7 @@ class Assets {
static const String emptyTable = 'assets/images/empty_table.svg';
// General assets
static const String motionlessDetection =
'assets/icons/motionless_detection.svg';
static const String motionlessDetection = 'assets/icons/motionless_detection.svg';
static const String acHeating = 'assets/icons/ac_heating.svg';
static const String acPowerOff = 'assets/icons/ac_power_off.svg';
static const String acFanMiddle = 'assets/icons/ac_fan_middle.svg';
@ -72,22 +70,19 @@ class Assets {
'assets/icons/automation_functions/temp_password_unlock.svg';
static const String doorlockNormalOpen =
'assets/icons/automation_functions/doorlock_normal_open.svg';
static const String doorbell =
'assets/icons/automation_functions/doorbell.svg';
static const String doorbell = 'assets/icons/automation_functions/doorbell.svg';
static const String remoteUnlockViaApp =
'assets/icons/automation_functions/remote_unlock_via_app.svg';
static const String doubleLock =
'assets/icons/automation_functions/double_lock.svg';
static const String selfTestResult =
'assets/icons/automation_functions/self_test_result.svg';
static const String lockAlarm =
'assets/icons/automation_functions/lock_alarm.svg';
static const String lockAlarm = 'assets/icons/automation_functions/lock_alarm.svg';
static const String presenceState =
'assets/icons/automation_functions/presence_state.svg';
static const String currentTemp =
'assets/icons/automation_functions/current_temp.svg';
static const String presence =
'assets/icons/automation_functions/presence.svg';
static const String presence = 'assets/icons/automation_functions/presence.svg';
static const String residualElectricity =
'assets/icons/automation_functions/residual_electricity.svg';
static const String hijackAlarm =
@ -104,15 +99,12 @@ class Assets {
// Presence Sensor Assets
static const String sensorMotionIcon = 'assets/icons/sensor_motion_ic.svg';
static const String sensorPresenceIcon =
'assets/icons/sensor_presence_ic.svg';
static const String sensorPresenceIcon = 'assets/icons/sensor_presence_ic.svg';
static const String sensorVacantIcon = 'assets/icons/sensor_vacant_ic.svg';
static const String illuminanceRecordIcon =
'assets/icons/illuminance_record_ic.svg';
static const String presenceRecordIcon =
'assets/icons/presence_record_ic.svg';
static const String helpDescriptionIcon =
'assets/icons/help_description_ic.svg';
static const String presenceRecordIcon = 'assets/icons/presence_record_ic.svg';
static const String helpDescriptionIcon = 'assets/icons/help_description_ic.svg';
static const String lightPulp = 'assets/icons/light_pulb.svg';
static const String acDevice = 'assets/icons/ac_device.svg';
@ -166,12 +158,10 @@ class Assets {
static const String unit = 'assets/icons/unit_icon.svg';
static const String villa = 'assets/icons/villa_icon.svg';
static const String iconEdit = 'assets/icons/icon_edit_icon.svg';
static const String textFieldSearch =
'assets/icons/textfield_search_icon.svg';
static const String textFieldSearch = 'assets/icons/textfield_search_icon.svg';
static const String roundedAddIcon = 'assets/icons/rounded_add_icon.svg';
static const String addIcon = 'assets/icons/add_icon.svg';
static const String smartThermostatIcon =
'assets/icons/smart_thermostat_icon.svg';
static const String smartThermostatIcon = 'assets/icons/smart_thermostat_icon.svg';
static const String smartLightIcon = 'assets/icons/smart_light_icon.svg';
static const String presenceSensor = 'assets/icons/presence_sensor.svg';
static const String Gang3SwitchIcon = 'assets/icons/3_Gang_switch_icon.svg';
@ -219,8 +209,7 @@ class Assets {
//assets/icons/water_leak_normal.svg
static const String waterLeakNormal = 'assets/icons/water_leak_normal.svg';
//assets/icons/water_leak_detected.svg
static const String waterLeakDetected =
'assets/icons/water_leak_detected.svg';
static const String waterLeakDetected = 'assets/icons/water_leak_detected.svg';
//assets/icons/automation_records.svg
static const String automationRecords = 'assets/icons/automation_records.svg';
@ -291,16 +280,13 @@ class Assets {
'assets/icons/functions_icons/sensitivity.svg';
static const String assetsSensitivityOperationIcon =
'assets/icons/functions_icons/sesitivity_operation_icon.svg';
static const String assetsAcPower =
'assets/icons/functions_icons/ac_power.svg';
static const String assetsAcPower = 'assets/icons/functions_icons/ac_power.svg';
static const String assetsAcPowerOFF =
'assets/icons/functions_icons/ac_power_off.svg';
static const String assetsChildLock =
'assets/icons/functions_icons/child_lock.svg';
static const String assetsFreezing =
'assets/icons/functions_icons/freezing.svg';
static const String assetsFanSpeed =
'assets/icons/functions_icons/fan_speed.svg';
static const String assetsFreezing = 'assets/icons/functions_icons/freezing.svg';
static const String assetsFanSpeed = 'assets/icons/functions_icons/fan_speed.svg';
static const String assetsAcCooling =
'assets/icons/functions_icons/ac_cooling.svg';
static const String assetsAcHeating =
@ -309,8 +295,7 @@ class Assets {
'assets/icons/functions_icons/celsius_degrees.svg';
static const String assetsTempreture =
'assets/icons/functions_icons/tempreture.svg';
static const String assetsAcFanLow =
'assets/icons/functions_icons/ac_fan_low.svg';
static const String assetsAcFanLow = 'assets/icons/functions_icons/ac_fan_low.svg';
static const String assetsAcFanMiddle =
'assets/icons/functions_icons/ac_fan_middle.svg';
static const String assetsAcFanHigh =
@ -329,8 +314,7 @@ class Assets {
'assets/icons/functions_icons/far_detection.svg';
static const String assetsFarDetectionFunction =
'assets/icons/functions_icons/far_detection_function.svg';
static const String assetsIndicator =
'assets/icons/functions_icons/indicator.svg';
static const String assetsIndicator = 'assets/icons/functions_icons/indicator.svg';
static const String assetsMotionDetection =
'assets/icons/functions_icons/motion_detection.svg';
static const String assetsMotionlessDetection =
@ -343,8 +327,7 @@ class Assets {
'assets/icons/functions_icons/master_state.svg';
static const String assetsSwitchAlarmSound =
'assets/icons/functions_icons/switch_alarm_sound.svg';
static const String assetsResetOff =
'assets/icons/functions_icons/reset_off.svg';
static const String assetsResetOff = 'assets/icons/functions_icons/reset_off.svg';
// Assets for automation_functions
static const String assetsCardUnlock =
@ -388,15 +371,13 @@ class Assets {
static const String activeUser = 'assets/icons/active_user.svg';
static const String deActiveUser = 'assets/icons/deactive_user.svg';
static const String invitedIcon = 'assets/icons/invited_icon.svg';
static const String rectangleCheckBox =
'assets/icons/rectangle_check_box.png';
static const String rectangleCheckBox = 'assets/icons/rectangle_check_box.png';
static const String CheckBoxChecked = 'assets/icons/box_checked.png';
static const String emptyBox = 'assets/icons/empty_box.png';
static const String completeProcessIcon =
'assets/icons/compleate_process_icon.svg';
static const String completedDoneIcon = 'assets/images/completed_done.svg';
static const String currentProcessIcon =
'assets/icons/current_process_icon.svg';
static const String currentProcessIcon = 'assets/icons/current_process_icon.svg';
static const String uncomplete_ProcessIcon =
'assets/icons/uncompleate_process_icon.svg';
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
@ -417,11 +398,9 @@ class Assets {
static const String successIcon = 'assets/icons/success_icon.svg';
static const String spaceLocationIcon = 'assets/icons/spaseLocationIcon.svg';
static const String scenesPlayIcon = 'assets/icons/scenesPlayIcon.png';
static const String scenesPlayIconCheck =
'assets/icons/scenesPlayIconCheck.png';
static const String scenesPlayIconCheck = 'assets/icons/scenesPlayIconCheck.png';
static const String presenceStateIcon = 'assets/icons/presence_state.svg';
static const String currentDistanceIcon =
'assets/icons/current_distance_icon.svg';
static const String currentDistanceIcon = 'assets/icons/current_distance_icon.svg';
static const String farDetectionIcon = 'assets/icons/far_detection_icon.svg';
static const String motionDetectionSensitivityIcon =
@ -444,44 +423,29 @@ class Assets {
static const String cpsMode4 = 'assets/icons/cps_mode4.svg';
static const String closeToMotion = 'assets/icons/close_to_motion.svg';
static const String farAwayMotion = 'assets/icons/far_away_motion.svg';
static const String communicationFault =
'assets/icons/communication_fault.svg';
static const String communicationFault = 'assets/icons/communication_fault.svg';
static const String radarFault = 'assets/icons/radar_fault.svg';
static const String selfTestingSuccess =
'assets/icons/self_testing_success.svg';
static const String selfTestingFailure =
'assets/icons/self_testing_failure.svg';
static const String selfTestingTimeout =
'assets/icons/self_testing_timeout.svg';
static const String selfTestingSuccess = 'assets/icons/self_testing_success.svg';
static const String selfTestingFailure = 'assets/icons/self_testing_failure.svg';
static const String selfTestingTimeout = 'assets/icons/self_testing_timeout.svg';
static const String movingSpeed = 'assets/icons/moving_speed.svg';
static const String boundary = 'assets/icons/boundary.svg';
static const String motionMeter = 'assets/icons/motion_meter.svg';
static const String spatialStaticValue =
'assets/icons/spatial_static_value.svg';
static const String spatialMotionValue =
'assets/icons/spatial_motion_value.svg';
static const String spatialStaticValue = 'assets/icons/spatial_static_value.svg';
static const String spatialMotionValue = 'assets/icons/spatial_motion_value.svg';
static const String presenceJudgementThrshold =
'assets/icons/presence_judgement_threshold.svg';
static const String spaceType = 'assets/icons/space_type.svg';
static const String sportsPara = 'assets/icons/sports_para.svg';
static const String sensitivityFeature1 =
'assets/icons/sensitivity_feature_1.svg';
static const String sensitivityFeature2 =
'assets/icons/sensitivity_feature_2.svg';
static const String sensitivityFeature3 =
'assets/icons/sensitivity_feature_3.svg';
static const String sensitivityFeature4 =
'assets/icons/sensitivity_feature_4.svg';
static const String sensitivityFeature5 =
'assets/icons/sensitivity_feature_5.svg';
static const String sensitivityFeature6 =
'assets/icons/sensitivity_feature_6.svg';
static const String sensitivityFeature7 =
'assets/icons/sensitivity_feature_7.svg';
static const String sensitivityFeature8 =
'assets/icons/sensitivity_feature_8.svg';
static const String sensitivityFeature9 =
'assets/icons/sensitivity_feature_9.svg';
static const String sensitivityFeature1 = 'assets/icons/sensitivity_feature_1.svg';
static const String sensitivityFeature2 = 'assets/icons/sensitivity_feature_2.svg';
static const String sensitivityFeature3 = 'assets/icons/sensitivity_feature_3.svg';
static const String sensitivityFeature4 = 'assets/icons/sensitivity_feature_4.svg';
static const String sensitivityFeature5 = 'assets/icons/sensitivity_feature_5.svg';
static const String sensitivityFeature6 = 'assets/icons/sensitivity_feature_6.svg';
static const String sensitivityFeature7 = 'assets/icons/sensitivity_feature_7.svg';
static const String sensitivityFeature8 = 'assets/icons/sensitivity_feature_8.svg';
static const String sensitivityFeature9 = 'assets/icons/sensitivity_feature_9.svg';
static const String deviceTagIcon = 'assets/icons/device_tag_ic.svg';
static const String targetConfirmTimeIcon =
'assets/icons/target_confirm_time_icon.svg';
@ -489,13 +453,10 @@ class Assets {
static const String indentLevelIcon = 'assets/icons/indent_level_icon.svg';
static const String triggerLevelIcon = 'assets/icons/trigger_level_icon.svg';
static const String blankCalendar = 'assets/icons/blank_calendar.svg';
static const String refreshStatusIcon =
'assets/icons/refresh_status_icon.svg';
static const String energyConsumedIcon =
'assets/icons/energy_consumed_icon.svg';
static const String refreshStatusIcon = 'assets/icons/refresh_status_icon.svg';
static const String energyConsumedIcon = 'assets/icons/energy_consumed_icon.svg';
static const String closeSettingsIcon =
'assets/icons/close_settings_icon.svg';
static const String closeSettingsIcon = 'assets/icons/close_settings_icon.svg';
static const String editNameIconSettings =
'assets/icons/edit_name_icon_settings.svg';
@ -508,4 +469,11 @@ class Assets {
static const String humidityAqiSidebar = 'assets/icons/humidity.svg';
static const String autocadOccupancyImage =
'assets/images/autocad_occupancy_image.png';
static const String emptyBarredChart = 'assets/icons/empty_barred_chart.svg';
static const String emptyEnergyManagementChart =
'assets/icons/empty_energy_management_chart.svg';
static const String emptyEnergyManagementPerDevice =
'assets/icons/empty_energy_management_per_device.svg';
static const String emptyHeatmap = 'assets/icons/empty_heatmap.svg';
static const String emptyRangeOfAqi = 'assets/icons/empty_range_of_aqi.svg';
}