Compare commits
30 Commits
sep_bug_fi
...
access_bug
Author | SHA1 | Date | |
---|---|---|---|
3102c3823a | |||
d5395f4fe7 | |||
13e90abf76 | |||
308c8a44c5 | |||
7ca6e60d02 | |||
853cccbcce | |||
03dfeabde4 | |||
62e03bac27 | |||
79ec890244 | |||
4653c519c4 | |||
66f6b1cba9 | |||
c06f3d5a58 | |||
813f2f2693 | |||
d05328e998 | |||
ab22e15736 | |||
9ea8e3be2b | |||
889d3dbd02 | |||
2b5249e985 | |||
e508849fa5 | |||
3748fc1419 | |||
ebde81b64d | |||
00277742d0 | |||
9733295dca | |||
4f833e86fb | |||
1e2e2bf4e6 | |||
b9bc2ec186 | |||
47f1a9a6cd | |||
7661f54427 | |||
4fabbf7a77 | |||
05d027f8ff |
5
assets/icons/1gang.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555643 0.555643 0 1.24111 0H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79675 39.5036 2.48221L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
|
||||
<path d="M38.7589 0H35.0356C35.721 0 36.2767 0.555643 36.2767 1.24111V38.7589C36.2767 39.4444 35.721 40 35.0356 40H38.7589C39.4444 40 40 39.4444 40 38.7589V1.24111C40 0.555643 39.4444 0 38.7589 0Z" fill="#D1D1D1"/>
|
||||
<path opacity="0.6" d="M21.375 31.8319V33.3213C21.375 34.0067 20.9553 34.5624 20.4375 34.5624H16.3125C15.7947 34.5624 15.375 34.0067 15.375 33.3213V31.8319C15.375 31.1465 15.7947 30.5908 16.3125 30.5908H20.4375C20.9553 30.5908 21.375 31.1465 21.375 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 933 B |
7
assets/icons/2gang.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M38.0142 39.2553L35.3682 40H20.9308H19.9999H19.0691H1.24111C0.555643 40 0 39.4444 0 38.7589V1.24111C0 0.555643 0.555643 0 1.24111 0H19.0682H20.1226H20.9543H35.4255L38.2625 1.24111C38.9479 1.24111 39.5036 1.79675 39.5036 2.48221L39.2553 38.0142C39.2553 38.6997 38.6997 39.2553 38.0142 39.2553Z" fill="#E9E9E9"/>
|
||||
<path d="M38.7589 0H35.0356C35.721 0 36.2767 0.555643 36.2767 1.24111V38.7589C36.2767 39.4444 35.721 40 35.0356 40H38.7589C39.4444 40 40 39.4444 40 38.7589V1.24111C40 0.555643 39.4444 0 38.7589 0Z" fill="#D1D1D1"/>
|
||||
<path opacity="0.6" d="M12.0284 31.8319V33.3213C12.0284 34.0067 11.6087 34.5624 11.0909 34.5624H6.96594C6.44816 34.5624 6.02844 34.0067 6.02844 33.3213V31.8319C6.02844 31.1465 6.44816 30.5908 6.96594 30.5908H11.0909C11.6087 30.5908 12.0284 31.1465 12.0284 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
<path opacity="0.6" d="M26.0285 31.8319V33.3213C26.0285 34.0067 26.4482 34.5624 26.966 34.5624H31.091C31.6088 34.5624 32.0285 34.0067 32.0285 33.3213V31.8319C32.0285 31.1465 31.6088 30.5908 31.091 30.5908H26.966C26.4482 30.5908 26.0285 31.1465 26.0285 31.8319Z" fill="#023DFE" fill-opacity="0.5"/>
|
||||
<path d="M19.0691 0H20.9308V40H19.0691V0Z" fill="#D1D1D1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
17
assets/icons/automation_records.svg
Normal file
@ -0,0 +1,17 @@
|
||||
<svg width="35" height="36" viewBox="0 0 35 36" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M26.7648 0H11.3236C9.61799 0 8.23535 1.38264 8.23535 3.08824V32.049C8.23535 33.7546 9.61799 35.1373 11.3236 35.1373H31.9118C33.6174 35.1373 35.0001 33.7546 35.0001 32.049V8.23529L26.7648 0Z" fill="#E4F2F9"/>
|
||||
<path d="M26.7642 0H21.6172V35.1373H31.9113C33.6169 35.1373 34.9995 33.7546 34.9995 32.049V8.23529L26.7642 0Z" fill="#BDE0F1"/>
|
||||
<path d="M26.7646 0V7.20588C26.7646 7.77439 27.2256 8.23529 27.7941 8.23529H34.9999L26.7646 0Z" fill="#77BEE2"/>
|
||||
<path d="M29.1675 20.7258H14.0695C13.5009 20.7258 13.04 20.2649 13.04 19.6964C13.04 19.1279 13.5009 18.667 14.0695 18.667H29.1675C29.736 18.667 30.1969 19.1279 30.1969 19.6964C30.1969 20.2649 29.736 20.7258 29.1675 20.7258Z" fill="#5F5F82"/>
|
||||
<path d="M29.1675 14.5491H14.0695C13.5009 14.5491 13.04 14.0882 13.04 13.5196C13.04 12.9511 13.5009 12.4902 14.0695 12.4902H29.1675C29.736 12.4902 30.1969 12.9511 30.1969 13.5196C30.1969 14.0882 29.736 14.5491 29.1675 14.5491Z" fill="#5F5F82"/>
|
||||
<path d="M29.1675 26.9026H14.0695C13.5009 26.9026 13.04 26.4417 13.04 25.8732C13.04 25.3047 13.5009 24.8438 14.0695 24.8438H29.1675C29.736 24.8438 30.1969 25.3047 30.1969 25.8732C30.1969 26.4417 29.736 26.9026 29.1675 26.9026Z" fill="#5F5F82"/>
|
||||
<path d="M30.1956 19.6954C30.1956 19.1269 29.7347 18.666 29.1662 18.666H21.6172V20.7248H29.1662C29.7347 20.7248 30.1956 20.2639 30.1956 19.6954Z" fill="#3C3C55"/>
|
||||
<path d="M30.1956 13.5196C30.1956 12.9511 29.7347 12.4902 29.1662 12.4902H21.6172V14.5491H29.1662C29.7347 14.5491 30.1956 14.0882 30.1956 13.5196Z" fill="#3C3C55"/>
|
||||
<path d="M29.1662 26.9016C29.7347 26.9016 30.1956 26.4407 30.1956 25.8722C30.1956 25.3037 29.7347 24.8428 29.1662 24.8428H21.6172V26.9016H29.1662Z" fill="#3C3C55"/>
|
||||
<path d="M9.26471 35.1368C4.15615 35.1368 0 30.9807 0 25.8721C0 20.7636 4.15615 16.6074 9.26471 16.6074C14.3733 16.6074 18.5294 20.7636 18.5294 25.8721C18.5294 30.9807 14.3733 35.1368 9.26471 35.1368Z" fill="#FF7350"/>
|
||||
<path d="M9.26465 16.6074V35.1368C14.3732 35.1368 18.5294 30.9807 18.5294 25.8721C18.5294 20.7636 14.3732 16.6074 9.26465 16.6074Z" fill="#FF4B20"/>
|
||||
<path d="M9.26448 18.666C5.29115 18.666 2.05859 21.8986 2.05859 25.8719C2.05859 29.8452 5.29115 33.0778 9.26448 33.0778C13.2378 33.0778 16.4704 29.8452 16.4704 25.8719C16.4704 21.8986 13.2378 18.666 9.26448 18.666Z" fill="#E4F2F9"/>
|
||||
<path d="M9.26465 18.666V33.0778C13.238 33.0778 16.4705 29.8452 16.4705 25.8719C16.4705 21.8986 13.238 18.666 9.26465 18.666Z" fill="#BDE0F1"/>
|
||||
<path d="M12.0099 24.8432H10.2942V21.755C10.2942 21.1865 9.83327 20.7256 9.26476 20.7256C8.69625 20.7256 8.23535 21.1865 8.23535 21.755V25.8726C8.23535 26.4412 8.69625 26.9021 9.26476 26.9021H12.0099C12.5784 26.9021 13.0393 26.4412 13.0393 25.8726C13.0393 25.3041 12.5784 24.8432 12.0099 24.8432Z" fill="#5F5F82"/>
|
||||
<path d="M12.0097 24.8432H10.2941V21.755C10.2941 21.1865 9.83316 20.7256 9.26465 20.7256V26.9021H12.0097C12.5783 26.9021 13.0392 26.4412 13.0392 25.8726C13.0392 25.3041 12.5783 24.8432 12.0097 24.8432Z" fill="#3C3C55"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
135
assets/icons/closed_door.svg
Normal file
@ -0,0 +1,135 @@
|
||||
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.39648 12.4951H35.6037V37.8285H2.39648V12.4951Z" fill="url(#paint0_linear_3415_983)"/>
|
||||
<path d="M2.40332 20.3789H14.3054V38.0005H2.40332V20.3789Z" fill="url(#paint1_linear_3415_983)"/>
|
||||
<g clip-path="url(#clip0_3415_983)">
|
||||
<path d="M2.40332 19.1719H35.5958V29.3425H2.40332V19.1719Z" fill="url(#paint2_linear_3415_983)"/>
|
||||
<path d="M2.45801 11.9824H35.6149V15.9314H2.45801V11.9824Z" fill="url(#paint3_linear_3415_983)"/>
|
||||
<path d="M2.45801 15.1123H35.6149V19.0613H2.45801V15.1123Z" fill="url(#paint4_linear_3415_983)"/>
|
||||
<path d="M2.45801 18.3154H35.6149V22.2644H2.45801V18.3154Z" fill="url(#paint5_linear_3415_983)"/>
|
||||
<path d="M2.45801 21.3965H35.6149V25.3455H2.45801V21.3965Z" fill="url(#paint6_linear_3415_983)"/>
|
||||
<path d="M2.45801 24.5264H35.6149V28.4754H2.45801V24.5264Z" fill="url(#paint7_linear_3415_983)"/>
|
||||
<path d="M2.45801 27.7305H35.6149V31.6795H2.45801V27.7305Z" fill="url(#paint8_linear_3415_983)"/>
|
||||
<path d="M2.45801 30.8594H35.6149V34.8084H2.45801V30.8594Z" fill="url(#paint9_linear_3415_983)"/>
|
||||
<path d="M2.45801 33.9893H35.6149V37.9383H2.45801V33.9893Z" fill="url(#paint10_linear_3415_983)"/>
|
||||
</g>
|
||||
<path d="M36.3054 9.43704L22.7255 0.961176C21.6987 0.320392 20.3494 0 19 0C17.6506 0 16.3013 0.320392 15.2745 0.961176L1.69458 9.43704C0.640486 10.095 0 11.2497 0 12.4923V35.6368C0 36.942 1.05804 38 2.36323 38C3.03367 38 3.57722 37.4565 3.57722 36.786V15.833C3.57722 14.4546 4.69464 13.3373 6.07292 13.3373H31.9271C33.3054 13.3373 34.4228 14.4546 34.4228 15.833V36.786C34.4228 37.4565 34.9663 38 35.6368 38C36.942 38 38 36.942 38 35.6368V12.4923C38 11.2497 37.3595 10.095 36.3054 9.43704Z" fill="url(#paint11_linear_3415_983)"/>
|
||||
<path d="M36.3054 9.43704L22.7255 0.961177C21.6987 0.320392 20.3494 0 19 0C17.6506 0 16.3013 0.320392 15.2745 0.961177L1.69458 9.43704C0.640486 10.095 0 11.2497 0 12.4923V16.0686C0 14.8261 0.640486 13.6713 1.69458 13.0134L15.2745 4.53743C16.3013 3.89664 17.6506 3.57625 19 3.57625C20.3494 3.57625 21.6987 3.89664 22.7255 4.53743L36.3054 13.0133C37.3595 13.6712 38 14.826 38 16.0686V12.4923C38 11.2497 37.3595 10.095 36.3054 9.43704Z" fill="url(#paint12_linear_3415_983)"/>
|
||||
<path d="M14.7543 8.44803H23.2456C23.7264 8.44803 24.1161 8.05827 24.1161 7.57753C24.1161 7.09672 23.7264 6.70703 23.2456 6.70703H14.7543C14.2735 6.70703 13.8838 7.09679 13.8838 7.57753C13.8838 8.05827 14.2735 8.44803 14.7543 8.44803Z" fill="url(#paint13_linear_3415_983)"/>
|
||||
<path d="M14.7543 11.6512H23.2456C23.7264 11.6512 24.1161 11.2614 24.1161 10.7807C24.1161 10.2998 23.7264 9.91016 23.2456 9.91016H14.7543C14.2735 9.91016 13.8838 10.2999 13.8838 10.7807C13.8838 11.2614 14.2735 11.6512 14.7543 11.6512Z" fill="url(#paint14_linear_3415_983)"/>
|
||||
<path d="M38 16.0685V35.6363C38 36.289 37.7355 36.8798 37.3078 37.3075C36.8801 37.7352 36.2893 37.9997 35.6366 37.9997C34.966 37.9997 34.4228 37.4565 34.4228 36.7859V15.8323C34.4228 14.4546 33.3051 13.337 31.9267 13.337H6.07329C4.69486 13.337 3.57722 14.4546 3.57722 15.8323V36.7859C3.57722 37.1212 3.44161 37.4245 3.2218 37.6443C3.002 37.8641 2.69875 37.9997 2.36345 37.9997C1.05804 37.9997 0 36.9417 0 35.6363V16.0685C0 14.8257 0.640784 13.6708 1.69435 13.0128L15.2745 4.53735C16.3013 3.89656 17.6506 3.57617 19 3.57617C20.3494 3.57617 21.6987 3.89656 22.7255 4.53735L36.3056 13.0128C37.3592 13.6708 38 14.8257 38 16.0685Z" fill="url(#paint15_linear_3415_983)"/>
|
||||
<path d="M19.0745 1.49121C16.8994 1.49121 14.7908 2.11724 12.9767 3.30173C12.4945 3.61653 12.3588 4.2626 12.6736 4.74476L14.39 6.46646C14.3263 6.69416 14.3564 6.94682 14.4952 7.16066L16.0917 8.77879C16.0317 8.96194 16.0515 9.16945 16.1637 9.34417L17.8679 11.0139C17.7547 11.4323 17.8613 11.8978 18.1898 12.2264L19.3006 13.3372H31.927C33.3054 13.3372 34.4227 14.4546 34.4227 15.8329V28.4594L38 32.0366V16.1358L25.1723 3.30173C23.3583 2.11732 21.2496 1.49121 19.0745 1.49121Z" fill="url(#paint16_linear_3415_983)"/>
|
||||
<path d="M21.7117 8.35279C20.9031 7.83324 19.9655 7.55859 19.0005 7.55859C18.0355 7.55859 17.0981 7.83324 16.2893 8.35272C15.981 8.55084 15.8917 8.96132 16.0897 9.26956C16.2878 9.57781 16.6982 9.66715 17.0066 9.46917C17.6009 9.08739 18.2904 8.88554 19.0005 8.88554C19.7107 8.88554 20.4002 9.08739 20.9945 9.46917C21.1055 9.54048 21.2296 9.57453 21.3525 9.57453C21.5708 9.57453 21.7846 9.46686 21.9113 9.26956C22.1094 8.96139 22.02 8.55084 21.7117 8.35279Z" fill="url(#paint17_linear_3415_983)"/>
|
||||
<path d="M23.3287 5.90666C22.0398 5.06947 20.543 4.62695 19.0002 4.62695C17.4574 4.62695 15.9607 5.06947 14.6717 5.90658C14.2766 6.1632 14.1643 6.69155 14.421 7.08667C14.6776 7.4818 15.206 7.59401 15.6011 7.3374C16.6127 6.68037 17.7881 6.33308 19.0002 6.33308C20.2123 6.33308 21.3878 6.68037 22.3994 7.3374C22.5429 7.43061 22.704 7.47517 22.8632 7.47517C23.1424 7.47517 23.416 7.33822 23.5794 7.08667C23.8361 6.69155 23.7238 6.16327 23.3287 5.90666Z" fill="url(#paint18_linear_3415_983)"/>
|
||||
<path d="M25.0982 3.22758C23.2841 2.0431 21.1755 1.41699 19.0003 1.41699C16.8252 1.41699 14.7166 2.04302 12.9024 3.22751C12.4203 3.54231 12.2846 4.18838 12.5994 4.67054C12.9142 5.15269 13.5603 5.28837 14.0424 4.97357C15.5167 4.01105 17.2311 3.5023 19.0003 3.5023C20.7696 3.5023 22.484 4.01105 23.9582 4.97364C24.134 5.08846 24.3317 5.14338 24.5272 5.14338C24.8676 5.14338 25.2013 4.97692 25.4012 4.67061C25.716 4.18846 25.5804 3.54238 25.0982 3.22758Z" fill="url(#paint19_linear_3415_983)"/>
|
||||
<path d="M19.0002 12.5189C19.6912 12.5189 20.2514 11.9588 20.2514 11.2678C20.2514 10.5768 19.6912 10.0166 19.0002 10.0166C18.3092 10.0166 17.749 10.5768 17.749 11.2678C17.749 11.9588 18.3092 12.5189 19.0002 12.5189Z" fill="url(#paint20_linear_3415_983)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3415_983" x1="19.0001" y1="22.0548" x2="19.0001" y2="35.447" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#62DBFB"/>
|
||||
<stop offset="0.1912" stop-color="#57D5FA"/>
|
||||
<stop offset="0.5232" stop-color="#3BC5F7"/>
|
||||
<stop offset="0.954" stop-color="#0DABF2"/>
|
||||
<stop offset="1" stop-color="#08A9F1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_3415_983" x1="6.55642" y1="29.1897" x2="2.75642" y2="29.1897" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0593FC" stop-opacity="0"/>
|
||||
<stop offset="0.6831" stop-color="#0389FC" stop-opacity="0.683"/>
|
||||
<stop offset="1" stop-color="#0182FC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_3415_983" x1="18.9996" y1="25.2444" x2="18.9996" y2="20.1772" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0593FC" stop-opacity="0"/>
|
||||
<stop offset="0.6831" stop-color="#0389FC" stop-opacity="0.683"/>
|
||||
<stop offset="1" stop-color="#0182FC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_3415_983" x1="19.0364" y1="13.4726" x2="19.0364" y2="15.5602" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_3415_983" x1="19.0364" y1="16.6025" x2="19.0364" y2="18.6901" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_3415_983" x1="19.0364" y1="19.8056" x2="19.0364" y2="21.8932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_3415_983" x1="19.0364" y1="22.8867" x2="19.0364" y2="24.9743" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_3415_983" x1="19.0364" y1="26.0166" x2="19.0364" y2="28.1042" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_3415_983" x1="19.0364" y1="29.2207" x2="19.0364" y2="31.3083" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_3415_983" x1="19.0364" y1="32.3496" x2="19.0364" y2="34.4372" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_3415_983" x1="19.0364" y1="35.4795" x2="19.0364" y2="37.5671" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_3415_983" x1="8.5956" y1="5.36746" x2="26.3289" y2="36.9596" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_3415_983" x1="19" y1="-5.51269" x2="19" y2="16.7501" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_3415_983" x1="18.9999" y1="8.21578" x2="18.9999" y2="6.61255" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_3415_983" x1="18.9999" y1="11.4189" x2="18.9999" y2="9.81568" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_3415_983" x1="13.1332" y1="14.0993" x2="9.70572" y2="7.31888" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_3415_983" x1="28.616" y1="14.9578" x2="17.9611" y2="0.279385" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint17_linear_3415_983" x1="18.4669" y1="7.38439" x2="19.3768" y2="9.98784" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint18_linear_3415_983" x1="18.2352" y1="3.84222" x2="19.2715" y2="7.60831" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint19_linear_3415_983" x1="18.1995" y1="1.17089" x2="19.3116" y2="5.24031" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint20_linear_3415_983" x1="17.7869" y1="10.0545" x2="19.7964" y2="12.064" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_3415_983">
|
||||
<rect width="33.2119" height="25.9556" fill="white" transform="translate(2.40332 11.9824)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 11 KiB |
29
assets/icons/door_delay.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="35" height="34" viewBox="0 0 35 34" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M17.5355 9.24578H10.4576C10.1702 9.24578 9.93727 9.01288 9.93727 8.72543C9.93727 8.43798 10.1702 8.20508 10.4576 8.20508H17.5355C17.8229 8.20508 18.0559 8.43798 18.0559 8.72543C18.0559 9.01288 17.8229 9.24578 17.5355 9.24578ZM6.9905 9.24578H0.520352C0.232969 9.24578 0 9.01288 0 8.72543C0 8.43798 0.2329 8.20508 0.520352 8.20508H6.9905C7.27788 8.20508 7.51085 8.43798 7.51085 8.72543C7.51085 9.01288 7.27788 9.24578 6.9905 9.24578Z" fill="#D9EEFF"/>
|
||||
<path d="M7.49238 12.8874H2.60141C2.31402 12.8874 2.08105 12.6545 2.08105 12.367C2.08105 12.0796 2.31396 11.8467 2.60141 11.8467H7.49238C7.77977 11.8467 8.01273 12.0796 8.01273 12.367C8.01267 12.6545 7.77977 12.8874 7.49238 12.8874Z" fill="#D9EEFF"/>
|
||||
<path d="M4.18018 20.1706H0.520352C0.232969 20.1706 0 19.9377 0 19.6502C0 19.3628 0.2329 19.1299 0.520352 19.1299H4.18018C4.46756 19.1299 4.70053 19.3628 4.70053 19.6502C4.70053 19.9377 4.46756 20.1706 4.18018 20.1706Z" fill="#D9EEFF"/>
|
||||
<path d="M8.2021 23.8132H2.20199C1.91461 23.8132 1.68164 23.5803 1.68164 23.2928C1.68164 23.0054 1.91454 22.7725 2.20199 22.7725H8.2021C8.48948 22.7725 8.72245 23.0054 8.72245 23.2928C8.72245 23.5802 8.48955 23.8132 8.2021 23.8132Z" fill="#D9EEFF"/>
|
||||
<path d="M6.26145 27.4548H3.71273C3.42535 27.4548 3.19238 27.2219 3.19238 26.9344C3.19238 26.647 3.42528 26.4141 3.71273 26.4141H6.26145C6.54883 26.4141 6.7818 26.647 6.7818 26.9344C6.78173 27.2219 6.54883 27.4548 6.26145 27.4548Z" fill="#D9EEFF"/>
|
||||
<path d="M14.4701 31.0964H6.78207C6.49469 31.0964 6.26172 30.8635 6.26172 30.576C6.26172 30.2886 6.49462 30.0557 6.78207 30.0557H14.4701C14.7575 30.0557 14.9905 30.2886 14.9905 30.576C14.9905 30.8634 14.7576 31.0964 14.4701 31.0964Z" fill="#D9EEFF"/>
|
||||
<path d="M8.83613 16.529H4.79281C4.50543 16.529 4.27246 16.2961 4.27246 16.0086C4.27246 15.7212 4.50536 15.4883 4.79281 15.4883H8.83613C9.12352 15.4883 9.35648 15.7212 9.35648 16.0086C9.35648 16.2961 9.12352 16.529 8.83613 16.529Z" fill="#D9EEFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.5953 3.04199L22.9434 7.74231C23.7969 7.84854 24.6523 8.03927 25.4999 8.31852C26.3475 8.59776 27.1499 8.95378 27.8992 9.3746L29.5512 4.67428L24.5953 3.04199Z" fill="#EFF6FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.2115 0.527932L22.5999 2.38444C22.4682 2.78564 22.6864 3.22225 23.0876 3.35398L24.2637 3.73945L29.2176 5.37181L30.3916 5.75927C30.7928 5.89107 31.2294 5.67074 31.3612 5.26954L31.9728 3.41304C32.1045 3.01184 31.8843 2.57523 31.4831 2.4435L24.1811 0.0382052C23.7799 -0.0935917 23.3432 0.126731 23.2115 0.527932Z" fill="#375E7D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.3903 5.75932C30.7915 5.89111 31.2281 5.67079 31.3599 5.26959L31.9715 3.41309C32.1032 3.01188 31.8829 2.57527 31.4818 2.44354L29.6725 1.84766C30.0738 1.97945 30.294 2.416 30.1622 2.8172L29.5506 4.6737C29.4189 5.0749 28.9823 5.29516 28.5811 5.16343H28.585L29.2163 5.37192L30.3903 5.75932Z" fill="#2B4D66"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3254 33.1402C24.3875 35.4648 31.9963 31.6259 34.3208 24.5655C36.368 18.3528 33.6404 11.7153 28.1456 8.62577C27.3964 8.20488 26.594 7.84893 25.7464 7.56968C24.8987 7.29043 24.0433 7.09964 23.1898 6.99348C20.2104 6.62181 17.2585 7.27075 14.7609 8.72414C12.0175 10.3211 9.82274 12.8915 8.75291 16.1443C6.42644 23.2046 10.2652 30.8156 17.3254 33.1402Z" fill="#375E7D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.8179 10.3919C19.3153 8.57866 13.386 11.5719 11.5728 17.0746C9.76154 22.5774 12.7527 28.5069 18.2553 30.3182C23.7579 32.1314 29.6872 29.1401 31.5004 23.6374L31.5063 23.6177C33.3057 18.1209 30.3145 12.2012 24.8179 10.3919Z" fill="#EFF6FF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.8169 10.3926L22.0833 18.6919C21.7666 19.6517 22.3763 20.5563 23.1865 20.8788L31.5053 23.6184C33.3047 18.1216 30.3135 12.2019 24.8169 10.3926Z" fill="#EB5468"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.5064 23.6184C33.0836 18.8001 30.9793 13.6573 26.7354 11.2422C27.4807 13.4665 27.8917 15.913 27.8917 18.4814C27.8917 19.7991 27.7836 21.0853 27.5771 22.3243L31.5064 23.6184Z" fill="#E5384F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.3205 24.5668C36.3677 18.3541 33.64 11.7166 28.1453 8.62706C27.5947 8.3163 27.0145 8.04293 26.4088 7.80894C26.1905 7.72437 25.9703 7.64569 25.746 7.57098C25.4825 7.48443 25.219 7.40575 24.9535 7.33691C25.6654 8.53266 26.2671 9.8444 26.7352 11.2427C30.9792 13.6578 33.0834 18.8006 31.5062 23.6189C31.5042 23.6248 31.5022 23.6307 31.5003 23.6386C30.3735 27.0566 27.6596 29.5051 24.4461 30.4353C23.5828 31.7294 22.5837 32.8701 21.4746 33.8199C27.1522 33.8475 32.4483 30.2524 34.3205 24.5668Z" fill="#2B4D66"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.4453 30.4347C27.6588 29.5045 30.3727 27.056 31.4995 23.6379L31.5054 23.6183L27.5761 22.3242C27.2103 24.5249 26.5377 26.5801 25.6173 28.415C25.2615 29.123 24.8701 29.7975 24.4453 30.4347Z" fill="#D9EEFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.1457 8.62614L29.2175 5.37135L28.5823 5.16285L27.4083 4.77539L26.4092 7.80802C27.0149 8.04208 27.5951 8.31545 28.1457 8.62614Z" fill="#D9EEFF"/>
|
||||
<path d="M18.2667 30.8037C18.2127 30.8037 18.1578 30.7952 18.1038 30.7774C17.8308 30.6875 17.6824 30.3934 17.7724 30.1204L18.3329 28.4192C18.4227 28.1462 18.7166 27.9978 18.9899 28.0879C19.2629 28.1778 19.4113 28.4719 19.3212 28.7449L18.7608 30.4461C18.6886 30.665 18.4852 30.8037 18.2667 30.8037Z" fill="#375E7D"/>
|
||||
<path d="M13.2968 18.1426C13.2428 18.1426 13.1879 18.1341 13.1337 18.1162L11.4346 17.5557C11.1617 17.4657 11.0134 17.1715 11.1035 16.8985C11.1935 16.6257 11.4874 16.4771 11.7607 16.5675L13.4598 17.128C13.7327 17.218 13.881 17.5122 13.7909 17.7852C13.7187 18.0039 13.5153 18.1426 13.2968 18.1426Z" fill="#375E7D"/>
|
||||
<path d="M17.6349 13.1075C17.4448 13.1075 17.2617 13.003 17.1701 12.8218L16.3617 11.2229C16.2321 10.9665 16.3349 10.6534 16.5914 10.5238C16.8478 10.3943 17.1609 10.497 17.2905 10.7534L18.0988 12.3523C18.2285 12.6087 18.1256 12.9217 17.8692 13.0514C17.7939 13.0894 17.7138 13.1075 17.6349 13.1075Z" fill="#375E7D"/>
|
||||
<path d="M26.2598 30.2062C26.0695 30.2062 25.8862 30.1015 25.7948 29.9201L24.9885 28.3211C24.8591 28.0646 24.9622 27.7516 25.2188 27.6223C25.4753 27.4928 25.7882 27.5959 25.9176 27.8526L26.7239 29.4515C26.8533 29.7081 26.7502 30.021 26.4936 30.1504C26.4185 30.1882 26.3385 30.2062 26.2598 30.2062Z" fill="#375E7D"/>
|
||||
<path d="M12.1947 25.5749C12.0044 25.5749 11.8211 25.4701 11.7297 25.2887C11.6003 25.0321 11.7034 24.7193 11.96 24.5899L13.5589 23.7835C13.8154 23.6539 14.1284 23.7571 14.2577 24.0138C14.3872 24.2704 14.2841 24.5832 14.0274 24.7126L12.4285 25.519C12.3534 25.5569 12.2735 25.5749 12.1947 25.5749Z" fill="#375E7D"/>
|
||||
<path d="M20.1233 19.7372C19.9981 19.7372 19.8727 19.6924 19.7729 19.6015L15.995 16.1578C15.7826 15.9641 15.7674 15.6351 15.9609 15.4227C16.1545 15.2103 16.4835 15.1952 16.696 15.3887L20.4739 18.8324C20.6862 19.026 20.7014 19.355 20.5079 19.5674C20.4053 19.68 20.2646 19.7372 20.1233 19.7372Z" fill="#375E7D"/>
|
||||
<path d="M21.9851 24.5472C21.7247 24.5472 21.5 24.3522 21.469 24.0872L21.2409 22.1344C21.2075 21.849 21.4118 21.5906 21.6973 21.5572C21.9817 21.5243 22.2411 21.7281 22.2745 22.0136L22.5026 23.9665C22.5359 24.2519 22.3316 24.5103 22.0461 24.5437C22.0257 24.546 22.0053 24.5472 21.9851 24.5472Z" fill="#375E7D"/>
|
||||
<path d="M21.5489 31.3714C20.3897 31.3714 19.2254 31.1861 18.0923 30.8128C12.3267 28.9149 9.18023 22.6793 11.0784 16.9125C11.999 14.1188 13.9519 11.8507 16.5773 10.5258C19.2032 9.2007 22.1875 8.97792 24.9806 9.89831C30.7348 11.7925 33.8839 18.0161 32.0028 23.7739L31.9987 23.7875C31.9974 23.7919 31.996 23.7964 31.9946 23.8008C31.0741 26.5942 29.1212 28.862 26.4957 30.1864C24.9349 30.9734 23.2474 31.3714 21.5489 31.3714ZM21.5263 10.3809C19.9878 10.3809 18.4597 10.7414 17.0461 11.4547C14.6687 12.6544 12.9004 14.7083 12.0668 17.2379C10.3481 22.4595 13.1971 28.1058 18.4178 29.8243C20.9472 30.6578 23.6495 30.4564 26.0269 29.2571C28.4023 28.0589 30.1696 26.008 31.004 23.4815L31.0079 23.4687C31.0091 23.4646 31.0104 23.4604 31.0117 23.4564C32.7189 18.2411 29.8674 12.6024 24.6551 10.8866C24.655 10.8866 24.6549 10.8865 24.6549 10.8865C23.6295 10.5486 22.5755 10.3809 21.5263 10.3809Z" fill="#FFC250"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.0847 18.6918C21.1762 18.3928 20.1968 18.8865 19.8978 19.7951C19.5989 20.7037 20.0925 21.6831 21.0011 21.982C21.9096 22.281 22.889 21.7874 23.188 20.8787C23.4869 19.9702 22.9933 18.9907 22.0847 18.6918Z" fill="#FFE07D"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.1879 20.8785C23.4868 19.97 22.9932 18.9905 22.0846 18.6916C21.6107 18.5362 21.117 18.5952 20.71 18.8174C21.5497 19.1557 21.9942 20.0938 21.7071 20.967C21.5635 21.3996 21.2665 21.7398 20.8949 21.9444C20.9303 21.9581 20.9657 21.9699 21.001 21.9818C21.9096 22.2808 22.889 21.7872 23.1879 20.8785Z" fill="#FFE07D"/>
|
||||
</svg>
|
After Width: | Height: | Size: 8.7 KiB |
104
assets/icons/opened_door.svg
Normal file
@ -0,0 +1,104 @@
|
||||
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.39648 12.4951H35.6037V37.8285H2.39648V12.4951Z" fill="url(#paint0_linear_3415_1104)"/>
|
||||
<path d="M2.40332 20.3789H14.3054V38.0005H2.40332V20.3789Z" fill="url(#paint1_linear_3415_1104)"/>
|
||||
<g clip-path="url(#clip0_3415_1104)">
|
||||
<path d="M2.45801 10.3135H35.6149V14.2625H2.45801V10.3135Z" fill="url(#paint2_linear_3415_1104)"/>
|
||||
<path d="M2.45801 13.4424H35.6149V17.3914H2.45801V13.4424Z" fill="url(#paint3_linear_3415_1104)"/>
|
||||
<path d="M2.45801 16.5723H35.6149V20.5213H2.45801V16.5723Z" fill="url(#paint4_linear_3415_1104)"/>
|
||||
</g>
|
||||
<path d="M36.3054 9.43704L22.7255 0.961176C21.6987 0.320392 20.3494 0 19 0C17.6506 0 16.3013 0.320392 15.2745 0.961176L1.69458 9.43704C0.640486 10.095 0 11.2497 0 12.4923V35.6368C0 36.942 1.05804 38 2.36323 38C3.03367 38 3.57722 37.4565 3.57722 36.786V15.833C3.57722 14.4546 4.69464 13.3373 6.07292 13.3373H31.9271C33.3054 13.3373 34.4228 14.4546 34.4228 15.833V36.786C34.4228 37.4565 34.9663 38 35.6368 38C36.942 38 38 36.942 38 35.6368V12.4923C38 11.2497 37.3595 10.095 36.3054 9.43704Z" fill="url(#paint5_linear_3415_1104)"/>
|
||||
<path d="M36.3054 9.43704L22.7255 0.961177C21.6987 0.320392 20.3494 0 19 0C17.6506 0 16.3013 0.320392 15.2745 0.961177L1.69458 9.43704C0.640486 10.095 0 11.2497 0 12.4923V16.0686C0 14.8261 0.640486 13.6713 1.69458 13.0134L15.2745 4.53743C16.3013 3.89664 17.6506 3.57625 19 3.57625C20.3494 3.57625 21.6987 3.89664 22.7255 4.53743L36.3054 13.0133C37.3595 13.6712 38 14.826 38 16.0686V12.4923C38 11.2497 37.3595 10.095 36.3054 9.43704Z" fill="url(#paint6_linear_3415_1104)"/>
|
||||
<path d="M14.7543 8.44803H23.2456C23.7264 8.44803 24.1161 8.05827 24.1161 7.57753C24.1161 7.09672 23.7264 6.70703 23.2456 6.70703H14.7543C14.2735 6.70703 13.8838 7.09679 13.8838 7.57753C13.8838 8.05827 14.2735 8.44803 14.7543 8.44803Z" fill="url(#paint7_linear_3415_1104)"/>
|
||||
<path d="M14.7543 11.6512H23.2456C23.7264 11.6512 24.1161 11.2614 24.1161 10.7807C24.1161 10.2998 23.7264 9.91016 23.2456 9.91016H14.7543C14.2735 9.91016 13.8838 10.2999 13.8838 10.7807C13.8838 11.2614 14.2735 11.6512 14.7543 11.6512Z" fill="url(#paint8_linear_3415_1104)"/>
|
||||
<path d="M38 16.0685V35.6363C38 36.289 37.7355 36.8798 37.3078 37.3075C36.8801 37.7352 36.2893 37.9997 35.6366 37.9997C34.966 37.9997 34.4228 37.4565 34.4228 36.7859V15.8323C34.4228 14.4546 33.3051 13.337 31.9267 13.337H6.07329C4.69486 13.337 3.57722 14.4546 3.57722 15.8323V36.7859C3.57722 37.1212 3.44161 37.4245 3.2218 37.6443C3.002 37.8641 2.69875 37.9997 2.36345 37.9997C1.05804 37.9997 0 36.9417 0 35.6363V16.0685C0 14.8257 0.640784 13.6708 1.69435 13.0128L15.2745 4.53735C16.3013 3.89656 17.6506 3.57617 19 3.57617C20.3494 3.57617 21.6987 3.89656 22.7255 4.53735L36.3056 13.0128C37.3592 13.6708 38 14.8257 38 16.0685Z" fill="url(#paint9_linear_3415_1104)"/>
|
||||
<path d="M19.0745 1.49121C16.8994 1.49121 14.7908 2.11724 12.9767 3.30173C12.4945 3.61653 12.3588 4.2626 12.6736 4.74476L14.39 6.46646C14.3263 6.69416 14.3564 6.94682 14.4952 7.16066L16.0917 8.77879C16.0317 8.96194 16.0515 9.16945 16.1637 9.34417L17.8679 11.0139C17.7547 11.4323 17.8613 11.8978 18.1898 12.2264L19.3006 13.3372H31.927C33.3054 13.3372 34.4227 14.4546 34.4227 15.8329V28.4594L38 32.0366V16.1358L25.1723 3.30173C23.3583 2.11732 21.2496 1.49121 19.0745 1.49121Z" fill="url(#paint10_linear_3415_1104)"/>
|
||||
<path d="M21.7117 8.35279C20.9031 7.83324 19.9655 7.55859 19.0005 7.55859C18.0355 7.55859 17.0981 7.83324 16.2893 8.35272C15.981 8.55084 15.8917 8.96132 16.0897 9.26956C16.2878 9.57781 16.6982 9.66715 17.0066 9.46917C17.6009 9.08739 18.2904 8.88554 19.0005 8.88554C19.7107 8.88554 20.4002 9.08739 20.9945 9.46917C21.1055 9.54048 21.2296 9.57453 21.3525 9.57453C21.5708 9.57453 21.7846 9.46686 21.9113 9.26956C22.1094 8.96139 22.02 8.55084 21.7117 8.35279Z" fill="url(#paint11_linear_3415_1104)"/>
|
||||
<path d="M23.3287 5.90666C22.0398 5.06947 20.543 4.62695 19.0002 4.62695C17.4574 4.62695 15.9607 5.06947 14.6717 5.90658C14.2766 6.1632 14.1643 6.69155 14.421 7.08667C14.6776 7.4818 15.206 7.59401 15.6011 7.3374C16.6127 6.68037 17.7881 6.33308 19.0002 6.33308C20.2123 6.33308 21.3878 6.68037 22.3994 7.3374C22.5429 7.43061 22.704 7.47517 22.8632 7.47517C23.1424 7.47517 23.416 7.33822 23.5794 7.08667C23.8361 6.69155 23.7238 6.16327 23.3287 5.90666Z" fill="url(#paint12_linear_3415_1104)"/>
|
||||
<path d="M25.0982 3.22758C23.2841 2.0431 21.1755 1.41699 19.0003 1.41699C16.8252 1.41699 14.7166 2.04302 12.9024 3.22751C12.4203 3.54231 12.2846 4.18838 12.5994 4.67054C12.9142 5.15269 13.5603 5.28837 14.0424 4.97357C15.5167 4.01105 17.2311 3.5023 19.0003 3.5023C20.7696 3.5023 22.484 4.01105 23.9582 4.97364C24.134 5.08846 24.3317 5.14338 24.5272 5.14338C24.8676 5.14338 25.2013 4.97692 25.4012 4.67061C25.716 4.18846 25.5804 3.54238 25.0982 3.22758Z" fill="url(#paint13_linear_3415_1104)"/>
|
||||
<path d="M19.0002 12.5189C19.6912 12.5189 20.2514 11.9588 20.2514 11.2678C20.2514 10.5768 19.6912 10.0166 19.0002 10.0166C18.3092 10.0166 17.749 10.5768 17.749 11.2678C17.749 11.9588 18.3092 12.5189 19.0002 12.5189Z" fill="url(#paint14_linear_3415_1104)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_3415_1104" x1="19.0001" y1="22.0548" x2="19.0001" y2="35.447" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#62DBFB"/>
|
||||
<stop offset="0.1912" stop-color="#57D5FA"/>
|
||||
<stop offset="0.5232" stop-color="#3BC5F7"/>
|
||||
<stop offset="0.954" stop-color="#0DABF2"/>
|
||||
<stop offset="1" stop-color="#08A9F1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_3415_1104" x1="6.55642" y1="29.1897" x2="2.75642" y2="29.1897" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0593FC" stop-opacity="0"/>
|
||||
<stop offset="0.6831" stop-color="#0389FC" stop-opacity="0.683"/>
|
||||
<stop offset="1" stop-color="#0182FC"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_3415_1104" x1="19.0364" y1="11.8037" x2="19.0364" y2="13.8913" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_3415_1104" x1="19.0364" y1="14.9326" x2="19.0364" y2="17.0202" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_3415_1104" x1="19.0364" y1="18.0625" x2="19.0364" y2="20.1501" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#017297"/>
|
||||
<stop offset="1" stop-color="#024C67"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_3415_1104" x1="8.5956" y1="5.36746" x2="26.3289" y2="36.9596" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_3415_1104" x1="19" y1="-5.51269" x2="19" y2="16.7501" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_3415_1104" x1="18.9999" y1="8.21578" x2="18.9999" y2="6.61255" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_3415_1104" x1="18.9999" y1="11.4189" x2="18.9999" y2="9.81568" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#EAF9FA"/>
|
||||
<stop offset="1" stop-color="#B3DAFE"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_3415_1104" x1="13.1332" y1="14.0993" x2="9.70572" y2="7.31888" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_3415_1104" x1="28.616" y1="14.9578" x2="17.9611" y2="0.279385" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#7BACDF" stop-opacity="0"/>
|
||||
<stop offset="1" stop-color="#7BACDF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_3415_1104" x1="18.4669" y1="7.38439" x2="19.3768" y2="9.98784" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_3415_1104" x1="18.2352" y1="3.84222" x2="19.2715" y2="7.60831" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_3415_1104" x1="18.1995" y1="1.17089" x2="19.3116" y2="5.24031" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_3415_1104" x1="17.7869" y1="10.0545" x2="19.7964" y2="12.064" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#AEFFD1"/>
|
||||
<stop offset="0.1201" stop-color="#A3F9CB"/>
|
||||
<stop offset="0.3288" stop-color="#87EAB9"/>
|
||||
<stop offset="0.6012" stop-color="#59D19D"/>
|
||||
<stop offset="0.9235" stop-color="#19AF77"/>
|
||||
<stop offset="1" stop-color="#09A76D"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_3415_1104">
|
||||
<rect width="33.2119" height="25.9556" fill="white" transform="translate(2.40332 11.9824)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 9.3 KiB |
20
assets/icons/preferences.svg
Normal file
@ -0,0 +1,20 @@
|
||||
<svg width="36" height="33" viewBox="0 0 36 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M32.7404 13.0391H30.5674V19.5582H32.7404C34.5406 19.5582 36 18.0988 36 16.2986C36 14.4984 34.5406 13.0391 32.7404 13.0391Z" fill="#A5A8AB"/>
|
||||
<path d="M30.5674 13.0391H3.25956C1.45934 13.0391 0 14.4984 0 16.2986C0 18.0988 1.45934 19.5582 3.25956 19.5582H30.5674C32.3676 19.5582 33.827 18.0988 33.827 16.2986C33.827 14.4984 32.3676 13.0391 30.5674 13.0391Z" fill="#C2C4C6"/>
|
||||
<path d="M32.7404 15.2119H30.5674V17.385H32.7404C33.3405 17.385 33.8269 16.8985 33.8269 16.2984C33.8269 15.6984 33.3405 15.2119 32.7404 15.2119Z" fill="#414851"/>
|
||||
<path d="M30.5672 15.2119H3.25937C2.65932 15.2119 2.17285 15.6984 2.17285 16.2984C2.17285 16.8985 2.65932 17.385 3.25937 17.385H30.5672C31.1673 17.385 31.6537 16.8985 31.6537 16.2984C31.6537 15.6984 31.1673 15.2119 30.5672 15.2119Z" fill="#62676F"/>
|
||||
<path d="M11.9521 10.8652V21.7304C14.9525 21.7304 17.3847 19.2981 17.3847 16.2978C17.3847 13.2975 14.9525 10.8652 11.9521 10.8652Z" fill="#FCBE64"/>
|
||||
<path d="M15.2117 16.2978C15.2117 13.2975 13.7523 10.8652 11.9521 10.8652C8.95181 10.8652 6.51953 13.2975 6.51953 16.2978C6.51953 19.2981 8.95181 21.7304 11.9521 21.7304C13.7523 21.7304 15.2117 19.2981 15.2117 16.2978Z" fill="#FBDB63"/>
|
||||
<path d="M32.7404 2.17285H30.5674V8.69197H32.7404C34.5406 8.69197 36 7.23263 36 5.43241C36 3.63219 34.5406 2.17285 32.7404 2.17285Z" fill="#A5A8AB"/>
|
||||
<path d="M30.5674 2.17285H3.25956C1.45934 2.17285 0 3.63219 0 5.43241C0 7.23263 1.45934 8.69197 3.25956 8.69197H30.5674C32.3676 8.69197 33.827 7.23263 33.827 5.43241C33.827 3.63219 32.3676 2.17285 30.5674 2.17285Z" fill="#C2C4C6"/>
|
||||
<path d="M32.7404 4.3457H30.5674V6.51874H32.7404C33.3405 6.51874 33.8269 6.03227 33.8269 5.43222C33.8269 4.83217 33.3405 4.3457 32.7404 4.3457Z" fill="#414851"/>
|
||||
<path d="M30.5672 4.3457H3.25937C2.65932 4.3457 2.17285 4.83217 2.17285 5.43222C2.17285 6.03227 2.65932 6.51874 3.25937 6.51874H30.5672C31.1673 6.51874 31.6537 6.03227 31.6537 5.43222C31.6537 4.83217 31.1673 4.3457 30.5672 4.3457Z" fill="#62676F"/>
|
||||
<path d="M24.0479 0V10.8652C27.0482 10.8652 29.4804 8.43291 29.4804 5.4326C29.4804 2.43228 27.0482 0 24.0479 0Z" fill="#FA342D"/>
|
||||
<path d="M27.3074 5.4326C27.3074 2.43228 25.848 0 24.0478 0C21.0475 0 18.6152 2.43228 18.6152 5.4326C18.6152 8.43291 21.0475 10.8652 24.0478 10.8652C25.848 10.8652 27.3074 8.43291 27.3074 5.4326Z" fill="#FE5E49"/>
|
||||
<path d="M32.7404 23.9033H30.5674V30.4224H32.7404C34.5406 30.4224 36 28.9631 36 27.1629C36 25.3627 34.5406 23.9033 32.7404 23.9033Z" fill="#A5A8AB"/>
|
||||
<path d="M30.5674 23.9033H3.25956C1.45934 23.9033 0 25.3627 0 27.1629C0 28.9631 1.45934 30.4224 3.25956 30.4224H30.5674C32.3676 30.4224 33.827 28.9631 33.827 27.1629C33.827 25.3627 32.3676 23.9033 30.5674 23.9033Z" fill="#C2C4C6"/>
|
||||
<path d="M32.7404 26.0762H30.5674V28.2492H32.7404C33.3405 28.2492 33.8269 27.7627 33.8269 27.1627C33.8269 26.5626 33.3405 26.0762 32.7404 26.0762Z" fill="#414851"/>
|
||||
<path d="M30.5672 26.0762H3.25937C2.65932 26.0762 2.17285 26.5626 2.17285 27.1627C2.17285 27.7627 2.65932 28.2492 3.25937 28.2492H30.5672C31.1673 28.2492 31.6537 27.7627 31.6537 27.1627C31.6537 26.5626 31.1673 26.0762 30.5672 26.0762Z" fill="#62676F"/>
|
||||
<path d="M24.0479 21.7295V32.5947C27.0482 32.5947 29.4804 30.1624 29.4804 27.1621C29.4804 24.1618 27.0482 21.7295 24.0479 21.7295Z" fill="#7D99E8"/>
|
||||
<path d="M27.3074 27.1621C27.3074 24.1618 25.848 21.7295 24.0478 21.7295C21.0475 21.7295 18.6152 24.1618 18.6152 27.1621C18.6152 30.1624 21.0475 32.5947 24.0478 32.5947C25.848 32.5947 27.3074 30.1624 27.3074 27.1621Z" fill="#83B3F1"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.5 KiB |
18
assets/icons/records.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.7853L0.0206433 9.9747C-0.0455284 9.78815 0.0527038 9.5815 0.239256 9.51335C6.29643 7.33481 13.1218 5.30864 18.9563 2.81914C19.2171 2.65467 19.4217 2.81716 19.534 3.12805L26.7725 21.7863L28.7381 27.2528C28.8043 27.4393 28.7081 27.6479 28.5215 27.7142L14.6702 32.6952L9.27087 34.6371C9.08432 34.7053 8.87774 34.607 8.80958 34.4204L4.62771 22.7853Z" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.46894 18.5343L6.16844 5.11787C6.13433 4.92127 6.26674 4.73472 6.46129 4.70061L22.3042 1.98442L25.9325 1.38662C26.1311 1.30035 26.454 1.40063 26.5302 1.84401L30.4493 21.415L31.4301 27.1403C31.4642 27.3349 31.3318 27.5235 31.1372 27.5576L16.6301 30.0451L10.974 31.016C10.7795 31.0481 10.5909 30.9157 10.5568 30.7211L8.46894 18.5343Z" 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.3418C4.40308 8.06401 2.27704 8.78014 0.239256 9.51234C0.0527037 9.58056 -0.0455284 9.78714 0.0206433 9.97369L4.62771 22.7843L8.80958 34.4194C8.87781 34.606 9.08439 34.7043 9.27087 34.636L14.6702 32.6942L26.9732 28.2709L6.54915 7.3418Z" 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.20318H16.5086C16.2169 8.20318 15.9805 7.96666 15.9805 7.67483C15.9805 7.38301 16.2169 7.14648 16.5086 7.14648H31.6094C31.9011 7.14648 32.1376 7.38301 32.1376 7.67483C32.1376 7.96666 31.9012 8.20318 31.6094 8.20318Z" fill="#9AAFB7"/>
|
||||
<path d="M31.6094 11.9571H16.5086C16.2169 11.9571 15.9805 11.7206 15.9805 11.4287C15.9805 11.1369 16.2169 10.9004 16.5086 10.9004H31.6094C31.9011 10.9004 32.1376 11.1369 32.1376 11.4287C32.1376 11.7206 31.9012 11.9571 31.6094 11.9571Z" fill="#9AAFB7"/>
|
||||
<path d="M31.6094 15.71H16.5086C16.2169 15.71 15.9805 15.4735 15.9805 15.1817C15.9805 14.8898 16.2169 14.6533 16.5086 14.6533H31.6094C31.9011 14.6533 32.1376 14.8898 32.1376 15.1817C32.1376 15.4735 31.9012 15.71 31.6094 15.71Z" fill="#9AAFB7"/>
|
||||
<path d="M31.6094 19.4639H16.5086C16.2169 19.4639 15.9805 19.2274 15.9805 18.9356C15.9805 18.6437 16.2169 18.4072 16.5086 18.4072H31.6094C31.9011 18.4072 32.1376 18.6437 32.1376 18.9356C32.1376 19.2274 31.9012 19.4639 31.6094 19.4639Z" 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 |
11
assets/icons/water_leak_detected.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="33" height="38" viewBox="0 0 33 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M29.788 23.1188C29.788 31.3375 23.1198 38 14.894 38C6.66832 38 0 31.3375 0 23.1188C0 15.8539 11.6379 3.1015 14.3389 0.239633C14.6404 -0.0798778 15.1477 -0.0798778 15.4492 0.239633C18.1502 3.10158 29.788 15.8539 29.788 23.1188Z" fill="#B3DAFE"/>
|
||||
<path d="M15.4491 0.239633C15.1476 -0.0798778 14.6403 -0.0798778 14.3388 0.239633C14.1736 0.414715 13.9745 0.627277 13.7471 0.872793C17.2371 4.64154 27.4942 16.2982 27.4942 23.1187C27.4942 30.9518 21.4369 37.3714 13.7471 37.9564C14.1256 37.9852 14.508 37.9999 14.894 37.9999C23.1197 37.9999 29.788 31.3374 29.788 23.1187C29.788 15.8539 18.1502 3.10158 15.4491 0.239633Z" fill="#8AC9FE"/>
|
||||
<path d="M29.7489 24.1975C25.598 22.4389 21.0994 22.7823 9.48614 25.2106C6.87119 25.7574 3.87944 26.4133 0.56543 27.1942C2.33903 33.4317 8.08252 38.0002 14.8941 38.0002C22.7568 38.0002 29.1958 31.9126 29.7489 24.1975Z" fill="#60B7FF"/>
|
||||
<path d="M32.164 10.3818C32.164 12.4699 31.3533 14.4352 29.8815 15.9156C28.4104 17.3949 26.4523 18.2168 24.3669 18.23C24.3617 18.23 24.3561 18.2304 24.3509 18.23C24.3393 18.2304 24.3274 18.2304 24.3154 18.2304C22.2192 18.2304 20.2482 17.4142 18.7658 15.9316C17.2832 14.4493 16.4668 12.4782 16.4668 10.3818C16.4668 8.28539 17.2832 6.31462 18.7658 4.83199C20.2482 3.34961 22.2192 2.5332 24.3154 2.5332C24.3274 2.5332 24.339 2.5332 24.3509 2.53356C24.3565 2.53356 24.362 2.53356 24.3675 2.5338C26.4526 2.54733 28.4107 3.36901 29.8815 4.84828C31.3533 6.32875 32.164 8.29389 32.164 10.3818Z" fill="#FF4756"/>
|
||||
<path d="M32.1636 10.3814C32.1636 12.4696 31.3529 14.4348 29.8811 15.9153C28.4101 17.3946 26.4519 18.2165 24.3665 18.2296C24.3614 18.2296 24.3557 18.23 24.3506 18.2296V2.5332C24.3561 2.5332 24.3616 2.5332 24.3671 2.53344C26.4523 2.54698 28.4103 3.36865 29.8811 4.84792C31.3529 6.32839 32.1636 8.29353 32.1636 10.3814Z" fill="#FF656F"/>
|
||||
<path d="M25.6615 7.14579V10.6543C25.6615 11.3755 25.0749 11.9622 24.3538 11.9622H24.3504C24.0136 11.9524 23.6658 11.8163 23.4289 11.5793C23.1821 11.3322 23.0459 11.0039 23.0459 10.6543V7.14579C23.0459 6.43262 23.638 5.85837 24.3504 5.83789H24.3538C25.0749 5.83789 25.6615 6.42471 25.6615 7.14579Z" fill="white"/>
|
||||
<path d="M25.5919 13.6205C25.5919 14.3033 25.0367 14.8585 24.3539 14.8585H24.3508C23.6699 14.8569 23.1162 14.3023 23.1162 13.6205C23.1162 12.939 23.6699 12.3844 24.3508 12.3828H24.3539C25.0367 12.3828 25.5919 12.938 25.5919 13.6205Z" fill="white"/>
|
||||
<path d="M25.5926 13.6215C25.5926 14.3043 25.0374 14.8595 24.3547 14.8595H24.3516V12.3838H24.3547C25.0374 12.3838 25.5926 12.939 25.5926 13.6215Z" fill="#DDEBF0"/>
|
||||
<path d="M25.6626 7.14676V10.6552C25.6626 11.3764 25.076 11.9631 24.3549 11.9631H24.3516V5.83887H24.3549C25.076 5.83887 25.6626 6.42569 25.6626 7.14676Z" fill="#DDEBF0"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.8 KiB |
11
assets/icons/water_leak_normal.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="33" height="38" viewBox="0 0 33 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M29.788 23.1188C29.788 31.3375 23.1198 38 14.894 38C6.66832 38 0 31.3375 0 23.1188C0 15.8539 11.6379 3.1015 14.3389 0.239633C14.6404 -0.0798778 15.1477 -0.0798778 15.4492 0.239633C18.1502 3.10158 29.788 15.8539 29.788 23.1188Z" fill="#B3DAFE"/>
|
||||
<path d="M15.4491 0.239633C15.1476 -0.0798778 14.6403 -0.0798778 14.3388 0.239633C14.1736 0.414715 13.9745 0.627277 13.7471 0.872793C17.2371 4.64154 27.4942 16.2982 27.4942 23.1187C27.4942 30.9518 21.4369 37.3714 13.7471 37.9564C14.1256 37.9852 14.508 37.9999 14.894 37.9999C23.1197 37.9999 29.788 31.3374 29.788 23.1187C29.788 15.8539 18.1502 3.10158 15.4491 0.239633Z" fill="#8AC9FE"/>
|
||||
<path d="M29.7489 24.1975C25.598 22.4389 21.0994 22.7823 9.48614 25.2106C6.87119 25.7574 3.87944 26.4133 0.56543 27.1942C2.33903 33.4317 8.08252 38.0002 14.8941 38.0002C22.7568 38.0002 29.1958 31.9126 29.7489 24.1975Z" fill="#60B7FF"/>
|
||||
<path d="M24.4503 18.2356C28.7786 18.2356 32.2874 14.7268 32.2874 10.3986C32.2874 6.07029 28.7786 2.56152 24.4503 2.56152C20.122 2.56152 16.6133 6.07029 16.6133 10.3986C16.6133 14.7268 20.122 18.2356 24.4503 18.2356Z" fill="#97D729"/>
|
||||
<path d="M24.4809 2.55176C24.0912 2.55176 23.7084 2.58063 23.334 2.63562C27.1139 3.19056 30.0157 6.44645 30.0157 10.3804C30.0157 14.3144 27.1139 17.5703 23.334 18.1252C23.7083 18.1802 24.0912 18.2091 24.4809 18.2091C28.8046 18.2091 32.3095 14.7041 32.3095 10.3804C32.3095 6.05673 28.8046 2.55176 24.4809 2.55176Z" fill="#8BC727"/>
|
||||
<path d="M23.5955 13.7687C23.152 13.7687 22.7351 13.596 22.4217 13.2823L20.0277 10.8884C19.5415 10.4023 19.5415 9.61415 20.0277 9.12794C20.5139 8.64181 21.3021 8.64166 21.7882 9.12794L23.5956 10.9352L27.174 7.35679C27.6602 6.87065 28.4484 6.87065 28.9345 7.35679C29.4207 7.84292 29.4207 8.6312 28.9345 9.11725L24.7693 13.2825C24.4557 13.5961 24.0389 13.7687 23.5955 13.7687Z" fill="#F9F7F8"/>
|
||||
<path d="M29.7489 24.1905C29.0405 23.8904 28.2551 23.6536 27.4901 23.4795C27.457 24.9292 27.2331 26.2952 26.8027 27.6092L28.6417 28.8525C28.9343 28.1675 29.2026 27.3281 29.4251 26.3498C29.6058 25.5549 29.7017 24.8258 29.7489 24.1905Z" fill="#26A6FE"/>
|
||||
<path d="M20.4549 26.3653C12.6679 25.0828 8.6078 20.1017 0.369386 20.6401C0.132109 21.5158 0 22.3486 0 23.1191C0 31.3378 6.66825 38.0003 14.894 38.0003C22.1215 38.0003 28.1457 32.8566 29.5018 26.0334C25.8167 26.8563 22.8019 26.7518 20.4549 26.3653Z" fill="#0593FC"/>
|
||||
<path d="M27.1164 26.4548C25.6828 32.7081 20.3073 37.4589 13.7412 37.9562C14.1215 37.9853 14.5057 38.0001 14.8935 38.0001C22.1209 38.0001 28.1451 32.8564 29.5012 26.0332C28.6723 26.2183 27.8776 26.3561 27.1164 26.4548Z" fill="#0182FC"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -2,11 +2,11 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart';
|
||||
import 'package:syncrow_web/pages/home/bloc/home_bloc.dart';
|
||||
import 'package:syncrow_web/pages/home/bloc/home_event.dart';
|
||||
import 'package:syncrow_web/pages/visitor_password/bloc/visitor_password_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/services/locator.dart';
|
||||
import 'package:syncrow_web/utils/app_routes.dart';
|
||||
import 'package:syncrow_web/utils/constants/routes_const.dart';
|
||||
@ -14,8 +14,7 @@ import 'package:syncrow_web/utils/theme/theme.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
try {
|
||||
const environment =
|
||||
String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||
await dotenv.load(fileName: '.env.$environment');
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
initialSetup();
|
||||
@ -45,11 +44,9 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//HomeBloc.fetchUserInfo();
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||
BlocProvider<VisitorPasswordBloc>(
|
||||
create: (context) => VisitorPasswordBloc(),
|
||||
)
|
||||
|
@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/access_management/bloc/access_state.dart';
|
||||
import 'package:syncrow_web/pages/access_management/model/password_model.dart';
|
||||
import 'package:syncrow_web/pages/common/hour_picker_dialog.dart';
|
||||
import 'package:syncrow_web/services/access_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/app_enum.dart';
|
||||
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||
|
||||
@ -64,13 +63,15 @@ 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());
|
||||
final DateTime? picked = await showDatePicker(
|
||||
context: event.context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime.now().add(const Duration(days: -5095)),
|
||||
firstDate: DateTime.now().add(const Duration(days: -5095)),
|
||||
lastDate: DateTime.now().add(const Duration(days: 2095)),
|
||||
);
|
||||
if (picked != null) {
|
||||
@ -97,7 +98,8 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
}
|
||||
} else {
|
||||
if (effectiveTimeTimeStamp != null && selectedTimestamp < effectiveTimeTimeStamp!) {
|
||||
CustomSnackBar.displaySnackBar('Expiration Time cannot be earlier than Effective Time.');
|
||||
CustomSnackBar.displaySnackBar(
|
||||
'Expiration Time cannot be earlier than Effective Time.');
|
||||
} else {
|
||||
endTime = selectedDateTime.toString().split('.').first;
|
||||
expirationTimeTimeStamp = selectedTimestamp;
|
||||
@ -106,14 +108,11 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
}
|
||||
}
|
||||
emit(ChangeTimeState());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<void> _filterData(FilterDataEvent event, Emitter<AccessState> emit) async {
|
||||
emit(AccessLoaded());
|
||||
try {
|
||||
print(event.emailAuthorizer?.toLowerCase());
|
||||
// Convert search text to lower case for case-insensitive search
|
||||
final searchText = event.passwordName?.toLowerCase() ?? '';
|
||||
final searchEmailText = event.emailAuthorizer?.toLowerCase() ?? '';
|
||||
@ -121,15 +120,17 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
bool matchesCriteria = true;
|
||||
// Convert timestamp to DateTime and extract date component
|
||||
DateTime effectiveDate =
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000)
|
||||
.toUtc()
|
||||
.toLocal();
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.effectiveTime.toString()) * 1000)
|
||||
.toUtc()
|
||||
.toLocal();
|
||||
DateTime invalidDate =
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000)
|
||||
.toUtc()
|
||||
.toLocal();
|
||||
DateTime effectiveDateAndTime = DateTime(effectiveDate.year, effectiveDate.month, effectiveDate.day,effectiveDate.hour,effectiveDate.minute);
|
||||
DateTime invalidDateAndTime = DateTime(invalidDate.year, invalidDate.month, invalidDate.day,invalidDate.hour,invalidDate.minute);
|
||||
DateTime.fromMillisecondsSinceEpoch(int.parse(item.invalidTime.toString()) * 1000)
|
||||
.toUtc()
|
||||
.toLocal();
|
||||
DateTime effectiveDateAndTime = DateTime(effectiveDate.year, effectiveDate.month,
|
||||
effectiveDate.day, effectiveDate.hour, effectiveDate.minute);
|
||||
DateTime invalidDateAndTime = DateTime(invalidDate.year, invalidDate.month, invalidDate.day,
|
||||
invalidDate.hour, invalidDate.minute);
|
||||
|
||||
// Filter by password name, making the search case-insensitive
|
||||
if (searchText.isNotEmpty) {
|
||||
@ -139,7 +140,8 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
}
|
||||
}
|
||||
if (searchEmailText.isNotEmpty) {
|
||||
final bool matchesName = item.authorizerEmail.toString().toLowerCase().contains(searchEmailText);
|
||||
final bool matchesName =
|
||||
item.authorizerEmail.toString().toLowerCase().contains(searchEmailText);
|
||||
if (!matchesName) {
|
||||
matchesCriteria = false;
|
||||
}
|
||||
@ -147,13 +149,9 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
// 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);
|
||||
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;
|
||||
}
|
||||
@ -161,14 +159,9 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
// Filter by end date only
|
||||
if (event.endTime != null && event.startTime == null) {
|
||||
DateTime startDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||
startDateTime = DateTime(
|
||||
startDateTime.year,
|
||||
startDateTime.month,
|
||||
startDateTime.day,
|
||||
startDateTime.hour,
|
||||
startDateTime.minute
|
||||
);
|
||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||
startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day,
|
||||
startDateTime.hour, startDateTime.minute);
|
||||
if (invalidDateAndTime.isAfter(startDateTime)) {
|
||||
matchesCriteria = false;
|
||||
}
|
||||
@ -177,12 +170,15 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
// Filter by both start date and end date
|
||||
if (event.startTime != null && event.endTime != null) {
|
||||
DateTime startDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000).toUtc().toLocal();
|
||||
DateTime endDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||
startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day,startDateTime.hour,startDateTime.minute);
|
||||
endDateTime = DateTime(endDateTime.year, endDateTime.month, endDateTime.day,endDateTime.hour,endDateTime.minute);
|
||||
if (effectiveDateAndTime.isBefore(startDateTime) || invalidDateAndTime.isAfter(endDateTime)) {
|
||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000).toUtc().toLocal();
|
||||
startDateTime = DateTime(startDateTime.year, startDateTime.month, startDateTime.day,
|
||||
startDateTime.hour, startDateTime.minute);
|
||||
endDateTime = DateTime(endDateTime.year, endDateTime.month, endDateTime.day,
|
||||
endDateTime.hour, endDateTime.minute);
|
||||
if (effectiveDateAndTime.isBefore(startDateTime) ||
|
||||
invalidDateAndTime.isAfter(endDateTime)) {
|
||||
matchesCriteria = false;
|
||||
}
|
||||
}
|
||||
@ -205,7 +201,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
|
||||
emit(AccessLoaded());
|
||||
startTime = 'Start Time';
|
||||
@ -224,7 +219,6 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
" ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
|
||||
}
|
||||
|
||||
|
||||
Future<void> onTabChanged(TabChangedEvent event, Emitter<AccessState> emit) async {
|
||||
try {
|
||||
emit(AccessLoaded());
|
||||
@ -257,6 +251,4 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||
emit(FailedState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,6 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
final isLargeScreen = isLargeScreenSize(context);
|
||||
final isSmallScreen = isSmallScreenSize(context);
|
||||
final isHalfMediumScreen = isHafMediumScreenSize(context);
|
||||
@ -86,7 +85,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
_buildVisitorAdminPasswords(context, accessBloc),
|
||||
const SizedBox(height: 20),
|
||||
Expanded(
|
||||
child: DynamicTable(
|
||||
child: DynamicTable(
|
||||
tableName: 'AccessManagement',
|
||||
uuidIndex: 1,
|
||||
withSelectAll: true,
|
||||
@ -169,7 +168,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
}
|
||||
|
||||
Row _buildNormalSearchWidgets(BuildContext context, AccessBloc accessBloc) {
|
||||
TimeOfDay _selectedTime = TimeOfDay.now();
|
||||
// TimeOfDay _selectedTime = TimeOfDay.now();
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -205,7 +204,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
title: 'Access Time',
|
||||
size: MediaQuery.of(context).size,
|
||||
endTime: () {
|
||||
accessBloc.add(SelectTime(context: context, isStart: false));
|
||||
accessBloc.add(SelectTime(context: context, isStart: false));
|
||||
},
|
||||
startTime: () {
|
||||
accessBloc.add(SelectTime(context: context, isStart: true));
|
||||
@ -218,7 +217,7 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
SearchResetButtons(
|
||||
onSearch: () {
|
||||
accessBloc.add(FilterDataEvent(
|
||||
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
|
||||
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
|
||||
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
||||
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
||||
startTime: accessBloc.effectiveTimeTimeStamp,
|
||||
@ -264,12 +263,11 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
SearchResetButtons(
|
||||
onSearch: () {
|
||||
accessBloc.add(FilterDataEvent(
|
||||
emailAuthorizer:accessBloc.emailAuthorizer.text.toLowerCase() ,
|
||||
emailAuthorizer: accessBloc.emailAuthorizer.text.toLowerCase(),
|
||||
selectedTabIndex: BlocProvider.of<AccessBloc>(context).selectedIndex,
|
||||
passwordName: accessBloc.passwordName.text.toLowerCase(),
|
||||
startTime: accessBloc.effectiveTimeTimeStamp,
|
||||
endTime: accessBloc.expirationTimeTimeStamp
|
||||
));
|
||||
endTime: accessBloc.expirationTimeTimeStamp));
|
||||
},
|
||||
onReset: () {
|
||||
accessBloc.add(ResetSearch());
|
||||
@ -278,6 +276,4 @@ class AccessManagementPage extends StatelessWidget with HelperResponsiveLayout {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -96,35 +96,22 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
|
||||
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||
emit(LoadingForgetState());
|
||||
try {
|
||||
var response = await AuthenticationAPI.verifyOtp(
|
||||
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
||||
if (response == true) {
|
||||
await AuthenticationAPI.forgetPassword(
|
||||
password: forgetPasswordController.text,
|
||||
email: forgetEmailController.text);
|
||||
_timer?.cancel();
|
||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
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());
|
||||
}
|
||||
try {
|
||||
var response = await AuthenticationAPI.verifyOtp(
|
||||
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
||||
if (response == true) {
|
||||
await AuthenticationAPI.forgetPassword(
|
||||
password: forgetPasswordController.text, email: forgetEmailController.text);
|
||||
_timer?.cancel();
|
||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||
emit(SuccessForgetState());
|
||||
}
|
||||
|
||||
} on DioException catch (e) {
|
||||
final errorData = e.response!.data;
|
||||
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
|
||||
validate = errorMessage;
|
||||
emit(AuthInitialState());
|
||||
}
|
||||
}
|
||||
|
||||
String? validateCode(String? value) {
|
||||
|
@ -48,8 +48,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
late ScrollController _scrollController;
|
||||
_scrollController = ScrollController();
|
||||
void _scrollToCenter() {
|
||||
final double middlePosition =
|
||||
_scrollController.position.maxScrollExtent / 2;
|
||||
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
||||
_scrollController.animateTo(
|
||||
middlePosition,
|
||||
duration: const Duration(seconds: 1),
|
||||
@ -66,8 +65,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
second: Center(
|
||||
child: Stack(
|
||||
children: [
|
||||
if (state is AuthLoading)
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
if (state is AuthLoading) const Center(child: CircularProgressIndicator()),
|
||||
ListView(
|
||||
shrinkWrap: true,
|
||||
controller: _scrollController,
|
||||
@ -97,21 +95,16 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
borderRadius:
|
||||
const BorderRadius.all(Radius.circular(30)),
|
||||
border: Border.all(
|
||||
color:
|
||||
ColorsManager.graysColor.withOpacity(0.2)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2)),
|
||||
),
|
||||
child: Form(
|
||||
key: forgetBloc.forgetFormKey,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: size.width * 0.02,
|
||||
vertical: size.width * 0.003),
|
||||
horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const SizedBox(height: 10),
|
||||
@ -128,66 +121,55 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Form(
|
||||
key: forgetBloc.forgetRegionKey,
|
||||
child: SizedBox(
|
||||
child: _buildDropdownField(
|
||||
context, forgetBloc, size)))
|
||||
Form(
|
||||
key: forgetBloc.forgetRegionKey,
|
||||
child: SizedBox(
|
||||
child:
|
||||
_buildDropdownField(context, forgetBloc, size)))
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Form(
|
||||
key: forgetBloc.forgetEmailKey,
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Account",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight:
|
||||
FontWeight.w400),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontSize: 14, fontWeight: FontWeight.w400),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
child: TextFormField(
|
||||
controller: forgetBloc.forgetEmailController,
|
||||
validator: forgetBloc.validateEmail,
|
||||
decoration:
|
||||
textBoxDecoration()!.copyWith(
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
hintText: 'Enter your email',
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: Colors.black),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
const SizedBox(height: 20.0),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
@ -195,35 +177,40 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
child: TextFormField(
|
||||
validator: forgetBloc.validateCode,
|
||||
keyboardType:
|
||||
TextInputType.visiblePassword,
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
controller: forgetBloc.forgetOtp,
|
||||
decoration:
|
||||
textBoxDecoration()!.copyWith(
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
hintText: 'Enter Code',
|
||||
hintStyle: Theme.of(context).textTheme
|
||||
.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
suffixIcon: SizedBox(
|
||||
width: 100,
|
||||
child: Center(
|
||||
child: InkWell(
|
||||
onTap: state is TimerState &&
|
||||
!state.isButtonEnabled &&
|
||||
state.remainingTime != 1
|
||||
!state.isButtonEnabled &&
|
||||
state.remainingTime != 1
|
||||
? null
|
||||
: () {
|
||||
if (forgetBloc.forgetEmailKey.currentState!.validate()||forgetBloc.forgetRegionKey.currentState!.validate()) {
|
||||
if(forgetBloc.forgetRegionKey.currentState!.validate()){
|
||||
if (forgetBloc
|
||||
.forgetEmailKey.currentState!
|
||||
.validate() ||
|
||||
forgetBloc
|
||||
.forgetRegionKey.currentState!
|
||||
.validate()) {
|
||||
if (forgetBloc
|
||||
.forgetRegionKey.currentState!
|
||||
.validate()) {
|
||||
forgetBloc.add(StartTimerEvent());
|
||||
}
|
||||
}
|
||||
@ -231,28 +218,23 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
child: Text(
|
||||
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
||||
style: TextStyle(
|
||||
color: state
|
||||
is TimerState &&
|
||||
!state
|
||||
.isButtonEnabled
|
||||
color: state is TimerState &&
|
||||
!state.isButtonEnabled
|
||||
? Colors.grey
|
||||
: ColorsManager
|
||||
.btnColor,
|
||||
: ColorsManager.btnColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: Colors.black),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
if (forgetBloc.forgetValidate !=
|
||||
'') // Check if there is a validation message
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 8.0),
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
forgetBloc.forgetValidate,
|
||||
style: const TextStyle(
|
||||
@ -265,8 +247,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
@ -274,35 +255,26 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
child: TextFormField(
|
||||
obscureText: forgetBloc.obscureText,
|
||||
keyboardType:
|
||||
TextInputType.visiblePassword,
|
||||
validator:
|
||||
forgetBloc.passwordValidator,
|
||||
controller: forgetBloc
|
||||
.forgetPasswordController,
|
||||
decoration:
|
||||
textBoxDecoration()!.copyWith(
|
||||
keyboardType: TextInputType.visiblePassword,
|
||||
validator: forgetBloc.passwordValidator,
|
||||
controller: forgetBloc.forgetPasswordController,
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
forgetBloc.add(
|
||||
PasswordVisibleEvent(
|
||||
newValue: forgetBloc
|
||||
.obscureText));
|
||||
forgetBloc.add(PasswordVisibleEvent(
|
||||
newValue: forgetBloc.obscureText));
|
||||
},
|
||||
icon: SizedBox(
|
||||
child: SvgPicture.asset(
|
||||
forgetBloc.obscureText
|
||||
? Assets.visiblePassword
|
||||
: Assets
|
||||
.invisiblePassword,
|
||||
: Assets.invisiblePassword,
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
@ -313,13 +285,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager.grayColor,
|
||||
fontWeight:
|
||||
FontWeight.w400),
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: Colors.black),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -329,21 +298,22 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 20.0),
|
||||
Row(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: size.width * 0.2,
|
||||
child: DefaultButton(
|
||||
backgroundColor:
|
||||
ColorsManager.btnColor,
|
||||
backgroundColor: ColorsManager.btnColor,
|
||||
child: const Text('Submit'),
|
||||
onPressed: () {
|
||||
if (forgetBloc.forgetFormKey.currentState!.validate() ||
|
||||
forgetBloc.forgetEmailKey.currentState!.validate() ) {
|
||||
if( forgetBloc.forgetEmailKey.currentState!.validate()
|
||||
&&forgetBloc.forgetFormKey.currentState!.validate() ){
|
||||
forgetBloc.forgetEmailKey.currentState!
|
||||
.validate()) {
|
||||
if (forgetBloc.forgetEmailKey.currentState!
|
||||
.validate() &&
|
||||
forgetBloc.forgetFormKey.currentState!
|
||||
.validate()) {
|
||||
forgetBloc.add(ChangePasswordEvent());
|
||||
}
|
||||
}
|
||||
@ -358,8 +328,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
child: Text(
|
||||
forgetBloc.validate,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.red),
|
||||
fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -372,8 +341,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
children: [
|
||||
const Text(
|
||||
"Do you have an account? ",
|
||||
style:
|
||||
TextStyle(color: Colors.white),
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
@ -407,8 +375,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildDropdownField(
|
||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -434,13 +401,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
builder: (FormFieldState<String> field) {
|
||||
return InputDecorator(
|
||||
decoration: InputDecoration(
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
|
||||
errorText: field.errorText,
|
||||
filled:
|
||||
true, // Ensure the dropdown is filled with the background color
|
||||
fillColor: ColorsManager
|
||||
.boxColor, // Match the dropdown container color
|
||||
filled: true, // Ensure the dropdown is filled with the background color
|
||||
fillColor: ColorsManager.boxColor, // Match the dropdown container color
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(
|
||||
@ -451,22 +415,20 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||
color: field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||
color: field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
borderSide: BorderSide(
|
||||
borderSide: const BorderSide(
|
||||
color: Colors.red,
|
||||
width: 1.5,
|
||||
),
|
||||
@ -488,24 +450,22 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
value: region.id,
|
||||
child: Text(
|
||||
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
region.name,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
value: loginBloc.regionList!
|
||||
.any((region) => region.id == loginBloc.regionUuid)
|
||||
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
|
||||
field.didChange(value); // Notify the form field of the change
|
||||
}
|
||||
},
|
||||
buttonStyleData: const ButtonStyleData(
|
||||
@ -529,8 +489,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
searchInnerWidgetHeight: 50,
|
||||
searchInnerWidget: Container(
|
||||
height: 50,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8, vertical: 4),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: TextFormField(
|
||||
style: const TextStyle(color: Colors.black),
|
||||
controller: textEditingController,
|
||||
@ -544,8 +503,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
searchMatchFn: (item, searchValue) {
|
||||
final regionName =
|
||||
(item.child as Text).data?.toLowerCase() ?? '';
|
||||
final regionName = (item.child as Text).data?.toLowerCase() ?? '';
|
||||
final search = searchValue.toLowerCase().trim();
|
||||
return regionName.contains(search);
|
||||
},
|
||||
@ -564,6 +522,4 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,8 +24,7 @@ class LoginWebPage extends StatefulWidget {
|
||||
State<LoginWebPage> createState() => _LoginWebPageState();
|
||||
}
|
||||
|
||||
class _LoginWebPageState extends State<LoginWebPage>
|
||||
with HelperResponsiveLayout {
|
||||
class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -60,8 +59,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
_scrollController = ScrollController();
|
||||
|
||||
void _scrollToCenter() {
|
||||
final double middlePosition =
|
||||
_scrollController.position.maxScrollExtent / 2;
|
||||
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
||||
_scrollController.animateTo(
|
||||
middlePosition,
|
||||
duration: const Duration(seconds: 1),
|
||||
@ -123,8 +121,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: _buildLoginFormFields(
|
||||
context, loginBloc, size),
|
||||
child: _buildLoginFormFields(context, loginBloc, size),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
@ -135,14 +132,12 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
),
|
||||
),
|
||||
),
|
||||
if (state is AuthLoading)
|
||||
const Center(child: CircularProgressIndicator())
|
||||
if (state is AuthLoading) const Center(child: CircularProgressIndicator())
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoginFormFields(
|
||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.1),
|
||||
@ -152,8 +147,8 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
child: Form(
|
||||
key: loginBloc.loginFormKey,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -181,9 +176,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildDropdownField(
|
||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -192,9 +185,9 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
Text(
|
||||
"Country/Region",
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Container(
|
||||
@ -211,9 +204,9 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
hint: Text(
|
||||
'Select your region/country',
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
items: loginBloc.regionList!.map((RegionModel region) {
|
||||
@ -227,7 +220,8 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
);
|
||||
}).toList(),
|
||||
value: loginBloc.regionList!.any(
|
||||
(region) => region.id == loginBloc.regionUuid,)
|
||||
(region) => region.id == loginBloc.regionUuid,
|
||||
)
|
||||
? loginBloc.regionUuid
|
||||
: null,
|
||||
onChanged: (String? value) {
|
||||
@ -286,7 +280,6 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildEmailField(BuildContext context, AuthBloc loginBloc) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -310,9 +303,10 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
errorStyle: const TextStyle(height: 0),
|
||||
hintText: 'Enter your email address',
|
||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
fontWeight: FontWeight.w400)),
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
),
|
||||
@ -344,18 +338,17 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
controller: loginBloc.loginPasswordController,
|
||||
decoration: textBoxDecoration()!.copyWith(
|
||||
hintText: 'At least 8 characters',
|
||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
loginBloc.add(
|
||||
PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
||||
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
||||
},
|
||||
icon: SizedBox(
|
||||
child: SvgPicture.asset(
|
||||
loginBloc.obscureText
|
||||
? Assets.visiblePassword
|
||||
: Assets.invisiblePassword,
|
||||
loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword,
|
||||
height: 15,
|
||||
width: 15,
|
||||
),
|
||||
@ -383,10 +376,10 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
},
|
||||
child: Text(
|
||||
"Forgot Password?",
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -450,8 +443,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSignInButton(
|
||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@ -492,8 +484,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
||||
SizedBox(
|
||||
child: Text(
|
||||
loginBloc.validate,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||
style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||
),
|
||||
)
|
||||
],
|
||||
|
304
lib/pages/common/access_device_table.dart
Normal file
@ -0,0 +1,304 @@
|
||||
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';
|
||||
|
||||
class AccessDeviceTable extends StatefulWidget {
|
||||
final List<String> headers;
|
||||
final String? tableName;
|
||||
final List<List<dynamic>> data;
|
||||
final BoxDecoration? headerDecoration;
|
||||
final BoxDecoration? cellDecoration;
|
||||
final Size size;
|
||||
final bool withCheckBox;
|
||||
final bool withSelectAll;
|
||||
final bool isEmpty;
|
||||
final void Function(bool?)? selectAll;
|
||||
final void Function(int, bool, dynamic)? onRowSelected;
|
||||
final List<String>? initialSelectedIds;
|
||||
final int uuidIndex;
|
||||
const AccessDeviceTable({
|
||||
super.key,
|
||||
required this.headers,
|
||||
required this.data,
|
||||
required this.size,
|
||||
this.tableName,
|
||||
required this.isEmpty,
|
||||
required this.withCheckBox,
|
||||
required this.withSelectAll,
|
||||
this.headerDecoration,
|
||||
this.cellDecoration,
|
||||
this.selectAll,
|
||||
this.onRowSelected,
|
||||
this.initialSelectedIds,
|
||||
required this.uuidIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
_DynamicTableState createState() => _DynamicTableState();
|
||||
}
|
||||
|
||||
class _DynamicTableState extends State<AccessDeviceTable> {
|
||||
late List<bool> _selected;
|
||||
bool _selectAll = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeSelection();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(AccessDeviceTable oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.data != widget.data) {
|
||||
_initializeSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void _initializeSelection() {
|
||||
if (widget.data.isEmpty) {
|
||||
_selected = [];
|
||||
_selectAll = false;
|
||||
} else {
|
||||
_selected = List<bool>.generate(widget.data.length, (index) {
|
||||
// Check if the initialSelectedIds contains the deviceUuid
|
||||
// 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);
|
||||
});
|
||||
_selectAll = _selected.every((element) => element == true);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
setState(() {
|
||||
_selectAll = value ?? false;
|
||||
_selected = List<bool>.filled(widget.data.length, _selectAll);
|
||||
if (widget.selectAll != null) {
|
||||
widget.selectAll!(_selectAll);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
decoration: widget.cellDecoration,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: widget.size.width,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: widget.headerDecoration ??
|
||||
BoxDecoration(color: Colors.grey[200]),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||
...widget.headers
|
||||
.map((header) => _buildTableHeaderCell(header)),
|
||||
],
|
||||
),
|
||||
),
|
||||
widget.isEmpty
|
||||
? Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyTable),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Text(
|
||||
// no password
|
||||
widget.tableName == 'AccessManagement'
|
||||
? 'No Password '
|
||||
: 'No Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
final row = widget.data[index];
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.withCheckBox)
|
||||
_buildRowCheckbox(
|
||||
index, widget.size.height * 0.10),
|
||||
...row.map((cell) => _buildTableCell(
|
||||
cell.toString(),
|
||||
widget.size.height * 0.10)),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectAllCheckbox() {
|
||||
return Container(
|
||||
width: 50,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(color: ColorsManager.boxDivider),
|
||||
),
|
||||
),
|
||||
child: Checkbox(
|
||||
value: widget.data.isNotEmpty &&
|
||||
_selected.every((element) => element == true),
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRowCheckbox(int index, double size) {
|
||||
return Container(
|
||||
width: 50,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
height: size,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: ColorsManager.boxDivider,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Center(
|
||||
child: Checkbox(
|
||||
value: _selected[index],
|
||||
onChanged: (bool? value) {
|
||||
_toggleRowSelection(index);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableHeaderCell(String title) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
border: Border.symmetric(
|
||||
vertical: BorderSide(color: ColorsManager.boxDivider),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 13,
|
||||
color: Color(0xFF999999),
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableCell(String content, double size) {
|
||||
bool isBatteryLevel = content.endsWith('%');
|
||||
double? batteryLevel;
|
||||
|
||||
if (isBatteryLevel) {
|
||||
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
||||
}
|
||||
|
||||
Color? statusColor;
|
||||
switch (content) {
|
||||
case 'Effective':
|
||||
statusColor = ColorsManager.textGreen;
|
||||
break;
|
||||
case 'Expired':
|
||||
statusColor = ColorsManager.red;
|
||||
break;
|
||||
case 'To be effective':
|
||||
statusColor = ColorsManager.yaGreen;
|
||||
break;
|
||||
case 'Online':
|
||||
statusColor = ColorsManager.green;
|
||||
break;
|
||||
case 'Offline':
|
||||
statusColor = ColorsManager.red;
|
||||
break;
|
||||
default:
|
||||
statusColor = Colors.black;
|
||||
}
|
||||
|
||||
return Expanded(
|
||||
child: Container(
|
||||
height: size,
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
color: ColorsManager.boxDivider,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
content,
|
||||
style: TextStyle(
|
||||
color: (batteryLevel != null && batteryLevel < 20)
|
||||
? ColorsManager.red
|
||||
: (batteryLevel != null && batteryLevel > 20)
|
||||
? ColorsManager.green
|
||||
: statusColor,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w400),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
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';
|
||||
|
||||
@ -22,13 +22,7 @@ class CurtainToggle extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/bloc/device_managment_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
@ -53,30 +55,47 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
@override
|
||||
void didUpdateWidget(DynamicTable oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.data.length != widget.data.length) {
|
||||
if (!_compareListOfLists(oldWidget.data, widget.data)) {
|
||||
_initializeSelection();
|
||||
}
|
||||
}
|
||||
|
||||
bool _compareListOfLists(List<List<dynamic>> oldList, List<List<dynamic>> newList) {
|
||||
// Check if the old and new lists are the same
|
||||
if (oldList.length != newList.length) return false;
|
||||
|
||||
for (int i = 0; i < oldList.length; i++) {
|
||||
if (oldList[i].length != newList[i].length) return false;
|
||||
|
||||
for (int j = 0; j < oldList[i].length; j++) {
|
||||
if (oldList[i][j] != newList[i][j]) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _initializeSelection() {
|
||||
_selectedRows = List<bool>.filled(widget.data.length, false);
|
||||
_selectAll = false;
|
||||
}
|
||||
|
||||
void _toggleSelectAll(bool? value) {
|
||||
setState(() {
|
||||
_selectAll = value ?? false;
|
||||
_selectedRows = List<bool>.filled(widget.data.length, _selectAll);
|
||||
});
|
||||
widget.onSelectionChanged?.call(_selectedRows);
|
||||
}
|
||||
|
||||
void _toggleRowSelection(int index) {
|
||||
setState(() {
|
||||
_selectedRows[index] = !_selectedRows[index];
|
||||
_selectAll = _selectedRows.every((isSelected) => isSelected);
|
||||
});
|
||||
widget.onSelectionChanged?.call(_selectedRows);
|
||||
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
||||
}
|
||||
|
||||
void _toggleSelectAll(bool? value) {
|
||||
setState(() {
|
||||
_selectAll = value ?? false;
|
||||
_selectedRows = List<bool>.filled(widget.data.length, _selectAll);
|
||||
});
|
||||
widget.onSelectionChanged?.call(_selectedRows);
|
||||
context.read<DeviceManagementBloc>().add(UpdateSelection(_selectedRows));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -90,13 +109,11 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: widget.headerDecoration ??
|
||||
BoxDecoration(color: Colors.grey[200]),
|
||||
decoration: widget.headerDecoration ?? BoxDecoration(color: Colors.grey[200]),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||
...widget.headers
|
||||
.map((header) => _buildTableHeaderCell(header)),
|
||||
...widget.headers.map((header) => _buildTableHeaderCell(header)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -117,14 +134,9 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
Text(
|
||||
// no password
|
||||
widget.tableName == 'AccessManagement'
|
||||
? 'No Password '
|
||||
: 'No Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color: ColorsManager.grayColor),
|
||||
widget.tableName == 'AccessManagement' ? 'No Password ' : 'No Devices',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodySmall!.copyWith(color: ColorsManager.grayColor),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -143,12 +155,8 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
final row = widget.data[index];
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.withCheckBox)
|
||||
_buildRowCheckbox(
|
||||
index, widget.size.height * 0.10),
|
||||
...row.map((cell) => _buildTableCell(
|
||||
cell.toString(),
|
||||
widget.size.height * 0.10)),
|
||||
if (widget.withCheckBox) _buildRowCheckbox(index, widget.size.height * 0.10),
|
||||
...row.map((cell) => _buildTableCell(cell.toString(), widget.size.height * 0.10)),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -173,9 +181,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
||||
),
|
||||
child: Checkbox(
|
||||
value: _selectAll,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty
|
||||
? _toggleSelectAll
|
||||
: null,
|
||||
onChanged: widget.withSelectAll && widget.data.isNotEmpty ? _toggleSelectAll : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -2,20 +2,21 @@ import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class StatefulTextField extends StatefulWidget {
|
||||
const StatefulTextField({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
required this.controller,
|
||||
});
|
||||
const StatefulTextField(
|
||||
{super.key,
|
||||
required this.title,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
required this.controller,
|
||||
this.onSubmitted});
|
||||
|
||||
final String title;
|
||||
final String hintText;
|
||||
final double width;
|
||||
final double elevation;
|
||||
final TextEditingController controller;
|
||||
final Function? onSubmitted;
|
||||
|
||||
@override
|
||||
State<StatefulTextField> createState() => _StatefulTextFieldState();
|
||||
@ -25,30 +26,31 @@ class _StatefulTextFieldState extends State<StatefulTextField> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CustomTextField(
|
||||
title: widget.title,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
width: widget.width,
|
||||
elevation: widget.elevation,
|
||||
);
|
||||
title: widget.title,
|
||||
controller: widget.controller,
|
||||
hintText: widget.hintText,
|
||||
width: widget.width,
|
||||
elevation: widget.elevation,
|
||||
onSubmittedFun: widget.onSubmitted);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTextField extends StatelessWidget {
|
||||
const CustomTextField({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.controller,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
});
|
||||
const CustomTextField(
|
||||
{super.key,
|
||||
required this.title,
|
||||
required this.controller,
|
||||
this.hintText = 'Please enter',
|
||||
required this.width,
|
||||
this.elevation = 0,
|
||||
this.onSubmittedFun});
|
||||
|
||||
final String title;
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
final double width;
|
||||
final double elevation;
|
||||
final Function? onSubmittedFun;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -81,10 +83,12 @@ class CustomTextField extends StatelessWidget {
|
||||
decoration: InputDecoration(
|
||||
hintText: hintText,
|
||||
hintStyle: const TextStyle(fontSize: 12),
|
||||
contentPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
onFieldSubmitted: (_) {
|
||||
onSubmittedFun!();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -15,8 +15,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class AcDeviceBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
class AcDeviceBatchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const AcDeviceBatchControlView({super.key, required this.devicesIds});
|
||||
|
||||
final List<String> devicesIds;
|
||||
@ -27,8 +26,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return BlocProvider(
|
||||
create: (context) => AcBloc(deviceId: devicesIds.first)
|
||||
..add(AcFetchBatchStatusEvent(devicesIds)),
|
||||
create: (context) => AcBloc(deviceId: devicesIds.first)..add(AcFetchBatchStatusEvent(devicesIds)),
|
||||
child: BlocBuilder<AcBloc, AcsState>(
|
||||
builder: (context, state) {
|
||||
if (state is ACStatusLoaded) {
|
||||
@ -66,6 +64,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
tempSet: state.status.tempSet,
|
||||
code: 'temp_set',
|
||||
devicesIds: devicesIds,
|
||||
isBatch: true,
|
||||
),
|
||||
BatchAcMode(
|
||||
value: state.status.acMode,
|
||||
@ -99,8 +98,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
),
|
||||
Text(
|
||||
'h',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
Text(
|
||||
'30',
|
||||
@ -109,9 +107,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text('m',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor)),
|
||||
Text('m', style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor)),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
@ -133,7 +129,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
code: 'child_lock',
|
||||
value: state.status.childLock,
|
||||
label: 'Child Lock',
|
||||
icon: state.status.childLock ? Assets.unlock : Assets.acLock,
|
||||
icon: state.status.childLock ? Assets.acLock : Assets.unlock,
|
||||
onChange: (value) {
|
||||
context.read<AcBloc>().add(AcBatchControlEvent(
|
||||
devicesIds: devicesIds,
|
||||
@ -147,8 +143,7 @@ class AcDeviceBatchControlView extends StatelessWidget
|
||||
callFactoryReset: () {
|
||||
context.read<AcBloc>().add(AcFactoryResetEvent(
|
||||
deviceId: state.status.uuid,
|
||||
factoryResetModel:
|
||||
FactoryResetModel(devicesUuid: devicesIds),
|
||||
factoryResetModel: FactoryResetModel(devicesUuid: devicesIds),
|
||||
));
|
||||
},
|
||||
),
|
||||
|
@ -24,8 +24,7 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return BlocProvider(
|
||||
create: (context) => AcBloc(deviceId: device.uuid!)
|
||||
..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
||||
create: (context) => AcBloc(deviceId: device.uuid!)..add(AcFetchDeviceStatusEvent(device.uuid!)),
|
||||
child: BlocBuilder<AcBloc, AcsState>(
|
||||
builder: (context, state) {
|
||||
if (state is ACStatusLoaded) {
|
||||
@ -98,8 +97,7 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
Text(
|
||||
'h',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
Text(
|
||||
'30',
|
||||
@ -108,9 +106,7 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text('m',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor)),
|
||||
Text('m', style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor)),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(
|
||||
@ -132,7 +128,7 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
code: 'child_lock',
|
||||
value: state.status.childLock,
|
||||
label: 'Child Lock',
|
||||
icon: state.status.childLock ? Assets.unlock : Assets.acLock,
|
||||
icon: state.status.childLock ? Assets.acLock : Assets.unlock,
|
||||
onChange: (value) {
|
||||
context.read<AcBloc>().add(
|
||||
AcControlEvent(
|
||||
|
@ -1,11 +1,12 @@
|
||||
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:flutter_svg/flutter_svg.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/model/ac_model.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';
|
||||
|
||||
class BatchAcMode extends StatelessWidget {
|
||||
const BatchAcMode({
|
||||
@ -21,30 +22,20 @@ class BatchAcMode extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
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),
|
||||
_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) {
|
||||
Widget _buildIconContainer(BuildContext context, TempModes mode, String assetPath, bool isSelected) {
|
||||
return Flexible(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
|
@ -1,11 +1,13 @@
|
||||
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_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/device_controls_container.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||
|
||||
class BatchCurrentTemp extends StatefulWidget {
|
||||
const BatchCurrentTemp({
|
||||
@ -14,12 +16,14 @@ class BatchCurrentTemp extends StatefulWidget {
|
||||
required this.devicesIds,
|
||||
required this.currentTemp,
|
||||
required this.tempSet,
|
||||
this.isBatch,
|
||||
});
|
||||
|
||||
final String code;
|
||||
final List<String> devicesIds;
|
||||
final int currentTemp;
|
||||
final int tempSet;
|
||||
final bool? isBatch;
|
||||
|
||||
@override
|
||||
State<BatchCurrentTemp> createState() => _CurrentTempState();
|
||||
@ -67,49 +71,39 @@ class _CurrentTempState extends State<BatchCurrentTemp> {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
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,
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
widget.isBatch == true
|
||||
? Text(
|
||||
'Set Temperature',
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey),
|
||||
)
|
||||
: 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(),
|
||||
|
@ -1,11 +1,12 @@
|
||||
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:flutter_svg/flutter_svg.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/model/ac_model.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';
|
||||
|
||||
class BatchFanSpeedControl extends StatelessWidget {
|
||||
const BatchFanSpeedControl({
|
||||
@ -21,23 +22,16 @@ class BatchFanSpeedControl extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
padding: 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),
|
||||
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto, value == FanSpeeds.auto),
|
||||
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -45,10 +39,8 @@ class BatchFanSpeedControl extends StatelessWidget {
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle,
|
||||
value == FanSpeeds.middle),
|
||||
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh,
|
||||
value == FanSpeeds.high),
|
||||
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle, value == FanSpeeds.middle),
|
||||
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh, value == FanSpeeds.high),
|
||||
],
|
||||
)
|
||||
],
|
||||
@ -56,8 +48,7 @@ class BatchFanSpeedControl extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
|
||||
String assetPath, bool isSelected) {
|
||||
Widget _buildIconContainer(BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context.read<AcBloc>().add(
|
||||
|
@ -1,11 +1,12 @@
|
||||
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:flutter_svg/flutter_svg.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/model/ac_model.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';
|
||||
|
||||
class AcMode extends StatelessWidget {
|
||||
const AcMode({
|
||||
@ -21,30 +22,20 @@ class AcMode extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
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),
|
||||
_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) {
|
||||
Widget _buildIconContainer(BuildContext context, TempModes mode, String assetPath, bool isSelected) {
|
||||
return Flexible(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
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/ac/bloc/ac_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_event.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';
|
||||
|
||||
@ -25,13 +25,7 @@ class AcToggle extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
@ -1,11 +1,13 @@
|
||||
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_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/device_controls_container.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/increament_decreament.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ac/bloc/ac_bloc.dart';
|
||||
|
||||
class CurrentTemp extends StatefulWidget {
|
||||
const CurrentTemp({
|
||||
@ -67,13 +69,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -83,10 +79,7 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
children: [
|
||||
Text(
|
||||
'Current Temperature',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: Colors.grey),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
@ -94,14 +87,8 @@ class _CurrentTempState extends State<CurrentTemp> {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
(widget.currentTemp > 99
|
||||
? widget.currentTemp / 10
|
||||
: widget.currentTemp)
|
||||
.toString(),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(color: Colors.grey),
|
||||
(widget.currentTemp > 99 ? widget.currentTemp / 10 : widget.currentTemp).toString(),
|
||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: Colors.grey),
|
||||
),
|
||||
const CelsiusSymbol(
|
||||
color: Colors.grey,
|
||||
|
@ -1,11 +1,12 @@
|
||||
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:flutter_svg/flutter_svg.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/model/ac_model.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';
|
||||
|
||||
class FanSpeedControl extends StatelessWidget {
|
||||
const FanSpeedControl({
|
||||
@ -21,23 +22,15 @@ class FanSpeedControl extends StatelessWidget {
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
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),
|
||||
_buildIconContainer(context, FanSpeeds.auto, Assets.acFanAuto, value == FanSpeeds.auto),
|
||||
_buildIconContainer(context, FanSpeeds.low, Assets.acFanLow, value == FanSpeeds.low),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -45,10 +38,8 @@ class FanSpeedControl extends StatelessWidget {
|
||||
runSpacing: 8,
|
||||
spacing: 8,
|
||||
children: [
|
||||
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle,
|
||||
value == FanSpeeds.middle),
|
||||
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh,
|
||||
value == FanSpeeds.high),
|
||||
_buildIconContainer(context, FanSpeeds.middle, Assets.acFanMiddle, value == FanSpeeds.middle),
|
||||
_buildIconContainer(context, FanSpeeds.high, Assets.acFanHigh, value == FanSpeeds.high),
|
||||
],
|
||||
)
|
||||
],
|
||||
@ -56,8 +47,7 @@ class FanSpeedControl extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildIconContainer(BuildContext context, FanSpeeds speed,
|
||||
String assetPath, bool isSelected) {
|
||||
Widget _buildIconContainer(BuildContext context, FanSpeeds speed, String assetPath, bool isSelected) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context.read<AcBloc>().add(
|
||||
|
@ -6,8 +6,7 @@ import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
part 'device_managment_event.dart';
|
||||
part 'device_managment_state.dart';
|
||||
|
||||
class DeviceManagementBloc
|
||||
extends Bloc<DeviceManagementEvent, DeviceManagementState> {
|
||||
class DeviceManagementBloc extends Bloc<DeviceManagementEvent, DeviceManagementState> {
|
||||
int _selectedIndex = 0;
|
||||
List<AllDevicesModel> _devices = [];
|
||||
int _onlineCount = 0;
|
||||
@ -15,7 +14,9 @@ class DeviceManagementBloc
|
||||
int _lowBatteryCount = 0;
|
||||
List<AllDevicesModel> _selectedDevices = [];
|
||||
List<AllDevicesModel> _filteredDevices = [];
|
||||
String productName = '';
|
||||
String currentProductName = '';
|
||||
String? currentCommunity;
|
||||
String? currentUnitName;
|
||||
|
||||
DeviceManagementBloc() : super(DeviceManagementInitial()) {
|
||||
on<FetchDevices>(_onFetchDevices);
|
||||
@ -28,8 +29,7 @@ class DeviceManagementBloc
|
||||
on<UpdateSelection>(_onUpdateSelection);
|
||||
}
|
||||
|
||||
Future<void> _onFetchDevices(
|
||||
FetchDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
Future<void> _onFetchDevices(FetchDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
emit(DeviceManagementLoading());
|
||||
try {
|
||||
final devices = await DevicesManagementApi().fetchDevices();
|
||||
@ -51,8 +51,7 @@ class DeviceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onFilterDevices(
|
||||
FilterDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
void _onFilterDevices(FilterDevices event, Emitter<DeviceManagementState> emit) async {
|
||||
if (_devices.isNotEmpty) {
|
||||
_filteredDevices = List.from(_devices.where((device) {
|
||||
switch (event.filter) {
|
||||
@ -77,15 +76,14 @@ class DeviceManagementBloc
|
||||
isControlButtonEnabled: _selectedDevices.isNotEmpty,
|
||||
));
|
||||
|
||||
if (productName.isNotEmpty) {
|
||||
add(SearchDevices(productName: productName));
|
||||
if (currentProductName.isNotEmpty) {
|
||||
add(SearchDevices(productName: currentProductName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onResetFilters(
|
||||
ResetFilters event, Emitter<DeviceManagementState> emit) async {
|
||||
productName = '';
|
||||
Future<void> _onResetFilters(ResetFilters event, Emitter<DeviceManagementState> emit) async {
|
||||
currentProductName = '';
|
||||
_selectedDevices.clear();
|
||||
_filteredDevices = List.from(_devices);
|
||||
_selectedIndex = 0;
|
||||
@ -100,8 +98,7 @@ class DeviceManagementBloc
|
||||
));
|
||||
}
|
||||
|
||||
void _onResetSelectedDevices(
|
||||
ResetSelectedDevices event, Emitter<DeviceManagementState> emit) {
|
||||
void _onResetSelectedDevices(ResetSelectedDevices event, Emitter<DeviceManagementState> emit) {
|
||||
_selectedDevices.clear();
|
||||
|
||||
if (state is DeviceManagementLoaded) {
|
||||
@ -127,14 +124,12 @@ class DeviceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectedFilterChanged(
|
||||
SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
|
||||
void _onSelectedFilterChanged(SelectedFilterChanged event, Emitter<DeviceManagementState> emit) {
|
||||
_selectedIndex = event.selectedIndex;
|
||||
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)) {
|
||||
@ -145,8 +140,7 @@ class DeviceManagementBloc
|
||||
|
||||
List<AllDevicesModel> clonedSelectedDevices = List.from(_selectedDevices);
|
||||
|
||||
bool isControlButtonEnabled =
|
||||
_checkIfControlButtonEnabled(clonedSelectedDevices);
|
||||
bool isControlButtonEnabled = _checkIfControlButtonEnabled(clonedSelectedDevices);
|
||||
|
||||
if (state is DeviceManagementLoaded) {
|
||||
emit(DeviceManagementLoaded(
|
||||
@ -155,8 +149,7 @@ class DeviceManagementBloc
|
||||
onlineCount: _onlineCount,
|
||||
offlineCount: _offlineCount,
|
||||
lowBatteryCount: _lowBatteryCount,
|
||||
selectedDevice:
|
||||
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||
selectedDevice: clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||
isControlButtonEnabled: isControlButtonEnabled,
|
||||
));
|
||||
} else if (state is DeviceManagementFiltered) {
|
||||
@ -166,15 +159,13 @@ class DeviceManagementBloc
|
||||
onlineCount: _onlineCount,
|
||||
offlineCount: _offlineCount,
|
||||
lowBatteryCount: _lowBatteryCount,
|
||||
selectedDevice:
|
||||
clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||
selectedDevice: clonedSelectedDevices.isNotEmpty ? clonedSelectedDevices : null,
|
||||
isControlButtonEnabled: isControlButtonEnabled,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateSelection(
|
||||
UpdateSelection event, Emitter<DeviceManagementState> emit) {
|
||||
void _onUpdateSelection(UpdateSelection event, Emitter<DeviceManagementState> emit) {
|
||||
List<AllDevicesModel> selectedDevices = [];
|
||||
List<AllDevicesModel> devicesToSelectFrom = [];
|
||||
|
||||
@ -217,8 +208,7 @@ class DeviceManagementBloc
|
||||
|
||||
bool _checkIfControlButtonEnabled(List<AllDevicesModel> selectedDevices) {
|
||||
if (selectedDevices.length > 1) {
|
||||
final productTypes =
|
||||
selectedDevices.map((device) => device.productType).toSet();
|
||||
final productTypes = selectedDevices.map((device) => device.productType).toSet();
|
||||
return productTypes.length == 1;
|
||||
} else if (selectedDevices.length == 1) {
|
||||
return true;
|
||||
@ -229,10 +219,8 @@ class DeviceManagementBloc
|
||||
void _calculateDeviceCounts() {
|
||||
_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)
|
||||
.length;
|
||||
_lowBatteryCount =
|
||||
_devices.where((device) => device.batteryLevel != null && device.batteryLevel! < 20).length;
|
||||
}
|
||||
|
||||
String _getFilterFromIndex(int index) {
|
||||
@ -248,53 +236,48 @@ 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.productName == null || event.productName!.isEmpty)) {
|
||||
productName = '';
|
||||
currentProductName = '';
|
||||
if (state is DeviceManagementFiltered) {
|
||||
add(FilterDevices(_getFilterFromIndex(_selectedIndex)));
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
productName = event.productName ?? '';
|
||||
if (event.productName == currentProductName &&
|
||||
event.community == currentCommunity &&
|
||||
event.unitName == currentUnitName &&
|
||||
event.searchField) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentProductName = event.productName ?? '';
|
||||
currentCommunity = event.community;
|
||||
currentUnitName = event.unitName;
|
||||
|
||||
List<AllDevicesModel> devicesToSearch = _filteredDevices;
|
||||
|
||||
if (devicesToSearch.isNotEmpty) {
|
||||
_selectedDevices.clear();
|
||||
_selectedIndex = _selectedIndex;
|
||||
|
||||
final filteredDevices = devicesToSearch.where((device) {
|
||||
final matchesCommunity = event.community == null ||
|
||||
event.community!.isEmpty ||
|
||||
(device.room?.name
|
||||
?.toLowerCase()
|
||||
.contains(event.community!.toLowerCase()) ??
|
||||
false);
|
||||
(device.room?.name?.toLowerCase().contains(event.community!.toLowerCase()) ?? false);
|
||||
final matchesUnit = event.unitName == null ||
|
||||
event.unitName!.isEmpty ||
|
||||
(device.unit?.name
|
||||
?.toLowerCase()
|
||||
.contains(event.unitName!.toLowerCase()) ??
|
||||
false);
|
||||
(device.unit?.name?.toLowerCase().contains(event.unitName!.toLowerCase()) ?? false);
|
||||
final matchesProductName = event.productName == null ||
|
||||
event.productName!.isEmpty ||
|
||||
(device.name
|
||||
?.toLowerCase()
|
||||
.contains(event.productName!.toLowerCase()) ??
|
||||
false);
|
||||
(device.name?.toLowerCase().contains(event.productName!.toLowerCase()) ?? false);
|
||||
final matchesDeviceName = event.productName == null ||
|
||||
event.productName!.isEmpty ||
|
||||
(device.categoryName
|
||||
?.toLowerCase()
|
||||
.contains(event.productName!.toLowerCase()) ??
|
||||
(device.categoryName?.toLowerCase().contains(event.productName!.toLowerCase()) ??
|
||||
false);
|
||||
|
||||
return matchesCommunity &&
|
||||
matchesUnit &&
|
||||
(matchesProductName || matchesDeviceName);
|
||||
return matchesCommunity && matchesUnit && (matchesProductName || matchesDeviceName);
|
||||
}).toList();
|
||||
|
||||
emit(DeviceManagementFiltered(
|
||||
|
@ -31,11 +31,13 @@ class SearchDevices extends DeviceManagementEvent {
|
||||
final String? community;
|
||||
final String? unitName;
|
||||
final String? productName;
|
||||
final bool searchField;
|
||||
|
||||
const SearchDevices({
|
||||
this.community,
|
||||
this.unitName,
|
||||
this.productName,
|
||||
this.searchField = false,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -9,20 +9,31 @@ import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_batch_st
|
||||
import 'package:syncrow_web/pages/device_managment/curtain/view/curtain_status_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/door_lock/view/door_lock_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/view/garage_door_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/gateway/view/gateway_batch_control.dart';
|
||||
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_g_glass_switch/view/one_gang_glass_batch_control_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_g_glass_switch/view/three_gang_glass_switch_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/view/three_gang_glass_switch_control_view.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/two_g_glass_switch/view/two_gang_glass_switch_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/view/two_gang_glass_switch_control_view.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/water_heater/view/water_heater_batch_control.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/view/water_heater_device_control.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_batch_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/view/water_leak_control_view.dart';
|
||||
|
||||
import '../../one_g_glass_switch/view/one_gang_glass_switch_control_view.dart';
|
||||
|
||||
mixin RouteControlsBasedCode {
|
||||
Widget routeControlsWidgets({required AllDevicesModel device}) {
|
||||
@ -39,6 +50,18 @@ mixin RouteControlsBasedCode {
|
||||
return LivingRoomDeviceControlsView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
case '1GT':
|
||||
return OneGangGlassSwitchControlView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
case '2GT':
|
||||
return TwoGangGlassSwitchControlView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
case '3GT':
|
||||
return ThreeGangGlassSwitchControlView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
case 'GW':
|
||||
return GateWayControlsView(
|
||||
gatewayId: device.uuid!,
|
||||
@ -63,6 +86,14 @@ mixin RouteControlsBasedCode {
|
||||
);
|
||||
case 'DS':
|
||||
return MainDoorSensorControlView(device: device);
|
||||
case 'GD':
|
||||
return GarageDoorControlView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
case 'WL':
|
||||
return WaterLeakView(
|
||||
deviceId: device.uuid!,
|
||||
);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
@ -105,6 +136,27 @@ mixin RouteControlsBasedCode {
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case '1GT':
|
||||
return OneGangGlassSwitchBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == '1GT'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case '2GT':
|
||||
return TwoGangGlassSwitchBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == '2GT'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case '3GT':
|
||||
return ThreeGangGlassSwitchBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == '3GT'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case 'GW':
|
||||
return GatewayBatchControlView(
|
||||
gatewayIds: devices
|
||||
@ -158,6 +210,20 @@ mixin RouteControlsBasedCode {
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case 'GD':
|
||||
return GarageDoorBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == 'GD'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
case 'WL':
|
||||
return WaterLeakBatchControlView(
|
||||
deviceIds: devices
|
||||
.where((e) => (e.productType == 'WL'))
|
||||
.map((e) => e.uuid!)
|
||||
.toList(),
|
||||
);
|
||||
default:
|
||||
return const SizedBox();
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import 'package:flutter/material.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/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/common/custom_table.dart';
|
||||
import 'package:syncrow_web/pages/common/filter/filter_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/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/shared/device_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_search_filters.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_batch_control_dialog.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/device_control_dialog.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
@ -37,8 +37,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
offlineCount = state.offlineCount;
|
||||
lowBatteryCount = state.lowBatteryCount;
|
||||
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||
selectedDevices = state.selectedDevice ??
|
||||
context.read<DeviceManagementBloc>().selectedDevices;
|
||||
selectedDevices = state.selectedDevice ?? [];
|
||||
} else if (state is DeviceManagementFiltered) {
|
||||
devicesToShow = state.filteredDevices;
|
||||
selectedIndex = state.selectedIndex;
|
||||
@ -46,14 +45,12 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
offlineCount = state.offlineCount;
|
||||
lowBatteryCount = state.lowBatteryCount;
|
||||
isControlButtonEnabled = state.isControlButtonEnabled;
|
||||
selectedDevices = state.selectedDevice ??
|
||||
context.read<DeviceManagementBloc>().selectedDevices;
|
||||
selectedDevices = state.selectedDevice ?? [];
|
||||
} else if (state is DeviceManagementInitial) {
|
||||
devicesToShow = [];
|
||||
selectedIndex = 0;
|
||||
isControlButtonEnabled = false;
|
||||
}
|
||||
|
||||
final tabs = [
|
||||
'All',
|
||||
'Online ($onlineCount)',
|
||||
@ -61,15 +58,12 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
'Low Battery ($lowBatteryCount)',
|
||||
];
|
||||
|
||||
final buttonLabel =
|
||||
(selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
final buttonLabel = (selectedDevices.length > 1) ? 'Batch Control' : 'Control';
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: isLargeScreenSize(context)
|
||||
? const EdgeInsets.all(30)
|
||||
: const EdgeInsets.all(15),
|
||||
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -78,9 +72,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
tabs: tabs,
|
||||
selectedIndex: selectedIndex,
|
||||
onTabChanged: (index) {
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(SelectedFilterChanged(index));
|
||||
context.read<DeviceManagementBloc>().add(SelectedFilterChanged(index));
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
@ -102,14 +94,11 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
);
|
||||
} else if (selectedDevices.length > 1) {
|
||||
final productTypes = selectedDevices
|
||||
.map((device) => device.productType)
|
||||
.toSet();
|
||||
final productTypes = selectedDevices.map((device) => device.productType).toSet();
|
||||
if (productTypes.length == 1) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
DeviceBatchControlDialog(
|
||||
builder: (context) => DeviceBatchControlDialog(
|
||||
devices: selectedDevices,
|
||||
),
|
||||
);
|
||||
@ -123,9 +112,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isControlButtonEnabled
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
color: isControlButtonEnabled ? Colors.white : Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -136,17 +123,13 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: isLargeScreenSize(context)
|
||||
? const EdgeInsets.all(30)
|
||||
: const EdgeInsets.all(15),
|
||||
padding: isLargeScreenSize(context) ? const EdgeInsets.all(30) : const EdgeInsets.all(15),
|
||||
child: DynamicTable(
|
||||
withSelectAll: true,
|
||||
cellDecoration: containerDecoration,
|
||||
onRowSelected: (index, isSelected, row) {
|
||||
final selectedDevice = devicesToShow[index];
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(SelectDevice(selectedDevice));
|
||||
context.read<DeviceManagementBloc>().add(SelectDevice(selectedDevice));
|
||||
},
|
||||
withCheckBox: true,
|
||||
size: context.screenSize,
|
||||
@ -169,26 +152,17 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
|
||||
device.uuid ?? '',
|
||||
device.unit?.name ?? '',
|
||||
device.room?.name ?? '',
|
||||
device.batteryLevel != null
|
||||
? '${device.batteryLevel}%'
|
||||
: '-',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.createTime ?? 0) * 1000)),
|
||||
device.batteryLevel != null ? '${device.batteryLevel}%' : '-',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.createTime ?? 0) * 1000)),
|
||||
device.online == true ? 'Online' : 'Offline',
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch(
|
||||
(device.updateTime ?? 0) * 1000)),
|
||||
formatDateTime(DateTime.fromMillisecondsSinceEpoch((device.updateTime ?? 0) * 1000)),
|
||||
];
|
||||
}).toList(),
|
||||
onSelectionChanged: (selectedRows) {
|
||||
context
|
||||
.read<DeviceManagementBloc>()
|
||||
.add(UpdateSelection(selectedRows));
|
||||
context.read<DeviceManagementBloc>().add(UpdateSelection(selectedRows));
|
||||
},
|
||||
initialSelectedIds: context
|
||||
.read<DeviceManagementBloc>()
|
||||
.selectedDevices
|
||||
.map((device) => device.uuid!)
|
||||
.toList(),
|
||||
initialSelectedIds:
|
||||
context.read<DeviceManagementBloc>().selectedDevices.map((device) => device.uuid!).toList(),
|
||||
isEmpty: devicesToShow.isEmpty,
|
||||
),
|
||||
),
|
||||
|
@ -12,8 +12,7 @@ class DeviceSearchFilters extends StatefulWidget {
|
||||
State<DeviceSearchFilters> createState() => _DeviceSearchFiltersState();
|
||||
}
|
||||
|
||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
with HelperResponsiveLayout {
|
||||
class _DeviceSearchFiltersState extends State<DeviceSearchFilters> with HelperResponsiveLayout {
|
||||
final TextEditingController communityController = TextEditingController();
|
||||
final TextEditingController unitNameController = TextEditingController();
|
||||
final TextEditingController productNameController = TextEditingController();
|
||||
@ -35,8 +34,7 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchField("Unit Name", unitNameController, 200),
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchField(
|
||||
"Device Name / Product Name", productNameController, 300),
|
||||
_buildSearchField("Device Name / Product Name", productNameController, 300),
|
||||
const SizedBox(width: 20),
|
||||
_buildSearchResetButtons(),
|
||||
],
|
||||
@ -45,22 +43,35 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
spacing: 20,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
_buildSearchField("Community", communityController, 200),
|
||||
_buildSearchField(
|
||||
"Community",
|
||||
communityController,
|
||||
200,
|
||||
),
|
||||
_buildSearchField("Unit Name", unitNameController, 200),
|
||||
_buildSearchField(
|
||||
"Device Name / Product Name", productNameController, 300),
|
||||
"Device Name / Product Name",
|
||||
productNameController,
|
||||
300,
|
||||
),
|
||||
_buildSearchResetButtons(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSearchField(
|
||||
String title, TextEditingController controller, double width) {
|
||||
Widget _buildSearchField(String title, TextEditingController controller, double width) {
|
||||
return StatefulTextField(
|
||||
title: title,
|
||||
width: width,
|
||||
elevation: 2,
|
||||
controller: controller,
|
||||
onSubmitted: () {
|
||||
context.read<DeviceManagementBloc>().add(SearchDevices(
|
||||
productName: productNameController.text,
|
||||
unitName: unitNameController.text,
|
||||
community: communityController.text,
|
||||
searchField: true));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,10 +79,10 @@ class _DeviceSearchFiltersState extends State<DeviceSearchFilters>
|
||||
return SearchResetButtons(
|
||||
onSearch: () {
|
||||
context.read<DeviceManagementBloc>().add(SearchDevices(
|
||||
community: communityController.text,
|
||||
unitName: unitNameController.text,
|
||||
productName: productNameController.text,
|
||||
));
|
||||
community: communityController.text,
|
||||
unitName: unitNameController.text,
|
||||
productName: productNameController.text,
|
||||
searchField: true));
|
||||
},
|
||||
onReset: () {
|
||||
communityController.clear();
|
||||
|
@ -1,8 +1,9 @@
|
||||
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/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/bloc/ceiling_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/help_description.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
@ -27,8 +28,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
CeilingInitialEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(CeilingLoadingInitialState());
|
||||
try {
|
||||
var response =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||
// _listenToChanges();
|
||||
@ -57,8 +57,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
// } catch (_) {}
|
||||
// }
|
||||
|
||||
void _changeValue(
|
||||
CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
void _changeValue(CeilingChangeValueEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(CeilingLoadingNewSate(ceilingSensorModel: deviceStatus));
|
||||
if (event.code == 'sensitivity') {
|
||||
deviceStatus.sensitivity = event.value;
|
||||
@ -123,8 +122,7 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
try {
|
||||
late bool response;
|
||||
if (isBatch) {
|
||||
response = await DevicesManagementApi()
|
||||
.deviceBatchControl(deviceId, code, value);
|
||||
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
|
||||
} else {
|
||||
response = await DevicesManagementApi()
|
||||
.deviceControl(deviceId, Status(code: code, value: value));
|
||||
@ -145,17 +143,19 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
});
|
||||
}
|
||||
|
||||
FutureOr<void> _getDeviceReports(GetCeilingDeviceReportsEvent event,
|
||||
Emitter<CeilingSensorState> emit) async {
|
||||
FutureOr<void> _getDeviceReports(
|
||||
GetCeilingDeviceReportsEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
if (event.code.isEmpty) {
|
||||
emit(ShowCeilingDescriptionState(description: reportString));
|
||||
return;
|
||||
} else {
|
||||
emit(CeilingReportsLoadingState());
|
||||
// final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
|
||||
// final to = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
try {
|
||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
||||
.then((value) {
|
||||
// await DevicesManagementApi.getDeviceReportsByDate(deviceId, event.code, from.toString(), to.toString())
|
||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) {
|
||||
emit(CeilingReportsState(deviceReport: value));
|
||||
});
|
||||
} catch (e) {
|
||||
@ -165,23 +165,19 @@ class CeilingSensorBloc extends Bloc<CeilingSensorEvent, CeilingSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _showDescription(
|
||||
ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
|
||||
void _showDescription(ShowCeilingDescriptionEvent event, Emitter<CeilingSensorState> emit) {
|
||||
emit(ShowCeilingDescriptionState(description: event.description));
|
||||
}
|
||||
|
||||
void _backToGridView(
|
||||
BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
|
||||
void _backToGridView(BackToCeilingGridViewEvent event, Emitter<CeilingSensorState> emit) {
|
||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||
}
|
||||
|
||||
FutureOr<void> _fetchCeilingSensorBatchControl(
|
||||
CeilingFetchDeviceStatusEvent event,
|
||||
Emitter<CeilingSensorState> emit) async {
|
||||
CeilingFetchDeviceStatusEvent event, Emitter<CeilingSensorState> emit) async {
|
||||
emit(CeilingLoadingInitialState());
|
||||
try {
|
||||
var response =
|
||||
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
deviceStatus = CeilingSensorModel.fromJson(response.status);
|
||||
emit(CeilingUpdateState(ceilingSensorModel: deviceStatus));
|
||||
} catch (e) {
|
@ -46,22 +46,19 @@ class CeilingSensorModel {
|
||||
_spaceType = getSpaceType(status.value ?? 'none');
|
||||
break;
|
||||
case 'sensitivity':
|
||||
_sensitivity = status.value is int
|
||||
? status.value
|
||||
: int.tryParse(status.value ?? '1') ?? 1;
|
||||
_sensitivity =
|
||||
status.value is int ? status.value : int.tryParse(status.value ?? '1') ?? 1;
|
||||
break;
|
||||
case 'checking_result':
|
||||
_checkingResult = status.value ?? '';
|
||||
break;
|
||||
case 'presence_range':
|
||||
_presenceRange = status.value is int
|
||||
? status.value
|
||||
: int.tryParse(status.value ?? '0') ?? 0;
|
||||
_presenceRange =
|
||||
status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0;
|
||||
break;
|
||||
case 'sports_para':
|
||||
_sportsPara = status.value is int
|
||||
? status.value
|
||||
: int.tryParse(status.value ?? '0') ?? 0;
|
||||
_sportsPara =
|
||||
status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0;
|
||||
break;
|
||||
case 'body_movement':
|
||||
_bodyMovement = status.value ?? '';
|
||||
@ -70,9 +67,7 @@ class CeilingSensorModel {
|
||||
_noBodyTime = status.value ?? 'none';
|
||||
break;
|
||||
case 'moving_max_dis':
|
||||
_maxDis = status.value is int
|
||||
? status.value
|
||||
: int.tryParse(status.value ?? '0') ?? 0;
|
||||
_maxDis = status.value is int ? status.value : int.tryParse(status.value ?? '0') ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
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/bloc/ceiling_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_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';
|
||||
@ -12,8 +12,7 @@ import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presen
|
||||
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 {
|
||||
class CeilingSensorBatchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const CeilingSensorBatchControlView({super.key, required this.devicesIds});
|
||||
|
||||
final List<String> devicesIds;
|
||||
@ -28,12 +27,11 @@ class CeilingSensorBatchControlView extends StatelessWidget
|
||||
..add(CeilingFetchDeviceStatusEvent(devicesIds)),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is CeilingLoadingInitialState ||
|
||||
state is CeilingReportsLoadingState) {
|
||||
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is CeilingUpdateState) {
|
||||
return _buildGridView(context, state.ceilingSensorModel,
|
||||
isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(
|
||||
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
@ -41,8 +39,8 @@ class CeilingSensorBatchControlView extends StatelessWidget
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
|
||||
bool isLarge, bool isMedium) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
@ -118,8 +116,7 @@ class CeilingSensorBatchControlView extends StatelessWidget
|
||||
context.read<CeilingSensorBloc>().add(
|
||||
CeilingFactoryResetEvent(
|
||||
devicesId: devicesIds.first,
|
||||
factoryResetModel:
|
||||
FactoryResetModel(devicesUuid: devicesIds),
|
||||
factoryResetModel: FactoryResetModel(devicesUuid: devicesIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/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/bloc/ceiling_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/bloc/ceiling_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/ceiling_sensor/model/ceiling_sensor_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_space_type.dart';
|
||||
@ -16,8 +16,7 @@ import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dar
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class CeilingSensorControlsView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
class CeilingSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const CeilingSensorControlsView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
@ -32,35 +31,29 @@ class CeilingSensorControlsView extends StatelessWidget
|
||||
..add(CeilingInitialEvent(device.uuid ?? '')),
|
||||
child: BlocBuilder<CeilingSensorBloc, CeilingSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is CeilingLoadingInitialState ||
|
||||
state is CeilingReportsLoadingState) {
|
||||
if (state is CeilingLoadingInitialState || state is CeilingReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is CeilingUpdateState) {
|
||||
return _buildGridView(context, state.ceilingSensorModel,
|
||||
isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(
|
||||
context, state.ceilingSensorModel, isExtraLarge, isLarge, isMedium);
|
||||
} else if (state is CeilingReportsState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(BackToCeilingGridViewEvent());
|
||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is ShowCeilingDescriptionState) {
|
||||
return DescriptionView(
|
||||
description: state.description,
|
||||
onClose: () {
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(BackToCeilingGridViewEvent());
|
||||
context.read<CeilingSensorBloc>().add(BackToCeilingGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is CeilingReportsFailedState) {
|
||||
final model = context.read<CeilingSensorBloc>().deviceStatus;
|
||||
return _buildGridView(
|
||||
context, model, isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
@ -68,8 +61,8 @@ class CeilingSensorControlsView extends StatelessWidget
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model,
|
||||
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
Widget _buildGridView(BuildContext context, CeilingSensorModel model, bool isExtraLarge,
|
||||
bool isLarge, bool isMedium) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
@ -150,8 +143,8 @@ class CeilingSensorControlsView extends StatelessWidget
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||
code: 'presence_state', deviceUuid: device.uuid!));
|
||||
context.read<CeilingSensorBloc>().add(
|
||||
GetCeilingDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.illuminanceRecordIcon,
|
||||
@ -160,8 +153,9 @@ class CeilingSensorControlsView extends StatelessWidget
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<CeilingSensorBloc>().add(GetCeilingDeviceReportsEvent(
|
||||
code: '', deviceUuid: device.uuid!));
|
||||
context
|
||||
.read<CeilingSensorBloc>()
|
||||
.add(GetCeilingDeviceReportsEvent(code: '', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.helpDescriptionIcon,
|
||||
|
@ -0,0 +1,423 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
|
||||
class GarageDoorBloc extends Bloc<GarageDoorEvent, GarageDoorState> {
|
||||
final String deviceId;
|
||||
late GarageDoorStatusModel deviceStatus;
|
||||
Timer? _timer;
|
||||
|
||||
GarageDoorBloc({required this.deviceId}) : super(GarageDoorInitialState()) {
|
||||
on<GarageDoorInitialEvent>(_fetchGarageDoorStatus);
|
||||
on<GarageDoorControlEvent>(_garageDoorControlEvent);
|
||||
on<AddGarageDoorScheduleEvent>(_addSchedule);
|
||||
on<UpdateGarageDoorScheduleEvent>(_updateSchedule);
|
||||
on<DeleteGarageDoorScheduleEvent>(_deleteSchedule);
|
||||
on<FetchGarageDoorSchedulesEvent>(_fetchSchedules);
|
||||
on<IncreaseGarageDoorDelayEvent>(_increaseDelay);
|
||||
on<DecreaseGarageDoorDelayEvent>(_decreaseDelay);
|
||||
on<FetchGarageDoorRecordsEvent>(_fetchRecords);
|
||||
on<GarageDoorUpdatedEvent>(_handleUpdate);
|
||||
on<UpdateSelectedTimeEvent>(_updateSelectedTime);
|
||||
on<UpdateSelectedDayEvent>(_updateSelectedDay);
|
||||
on<UpdateFunctionOnEvent>(_updateFunctionOn);
|
||||
on<InitializeAddScheduleEvent>(_initializeAddSchedule);
|
||||
on<BackToGarageDoorGridViewEvent>(_backToGridView);
|
||||
on<UpdateCountdownAlarmEvent>(_onUpdateCountdownAlarm);
|
||||
on<UpdateTrTimeConEvent>(_onUpdateTrTimeCon);
|
||||
on<GarageDoorBatchControlEvent>(_onBatchControl);
|
||||
on<GarageDoorFetchBatchStatusEvent>(_onFetchBatchStatus);
|
||||
on<GarageDoorFactoryResetEvent>(_onFactoryReset);
|
||||
on<EditGarageDoorScheduleEvent>(_onEditSchedule);
|
||||
}
|
||||
|
||||
void _fetchGarageDoorStatus(GarageDoorInitialEvent event, Emitter<GarageDoorState> emit) async {
|
||||
emit(GarageDoorLoadingState());
|
||||
try {
|
||||
var response = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = GarageDoorStatusModel.fromJson(deviceId, response.status);
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
} catch (e) {
|
||||
emit(GarageDoorErrorState(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFetchBatchStatus(GarageDoorFetchBatchStatusEvent event, Emitter<GarageDoorState> emit) async {
|
||||
emit(GarageDoorLoadingState());
|
||||
try {
|
||||
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
|
||||
deviceStatus = GarageDoorStatusModel.fromJson(event.deviceIds.first, status.status);
|
||||
emit(GarageDoorBatchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(GarageDoorBatchControlError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _addSchedule(AddGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
|
||||
try {
|
||||
ScheduleEntry newSchedule = ScheduleEntry(
|
||||
category: event.category,
|
||||
time: formatTimeOfDayToISO(event.time),
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
|
||||
);
|
||||
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, deviceId);
|
||||
if (success) {
|
||||
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'));
|
||||
} else {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateCountdownAlarm(UpdateCountdownAlarmEvent event, Emitter<GarageDoorState> emit) {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
emit(currentState.copyWith(
|
||||
status: currentState.status.copyWith(countdownAlarm: event.countdownAlarm),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateTrTimeCon(UpdateTrTimeConEvent event, Emitter<GarageDoorState> emit) {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
emit(currentState.copyWith(
|
||||
status: currentState.status.copyWith(trTimeCon: event.trTimeCon),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateSchedule(UpdateGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
|
||||
try {
|
||||
final updatedSchedules = deviceStatus.schedules?.map((schedule) {
|
||||
if (schedule.scheduleId == event.scheduleId) {
|
||||
return schedule.copyWith(
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
enable: event.enable,
|
||||
);
|
||||
}
|
||||
return schedule;
|
||||
}).toList();
|
||||
bool success = await DevicesManagementApi().updateScheduleRecord(
|
||||
enable: event.enable,
|
||||
uuid: deviceStatus.uuid,
|
||||
scheduleId: event.scheduleId,
|
||||
);
|
||||
if (success) {
|
||||
deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules);
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
} else {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _deleteSchedule(DeleteGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
|
||||
try {
|
||||
bool success = await DevicesManagementApi().deleteScheduleRecord(deviceStatus.uuid, event.scheduleId);
|
||||
if (success) {
|
||||
final updatedSchedules =
|
||||
deviceStatus.schedules?.where((schedule) => schedule.scheduleId != event.scheduleId).toList();
|
||||
deviceStatus = deviceStatus.copyWith(schedules: updatedSchedules);
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
} else {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchSchedules(FetchGarageDoorSchedulesEvent event, Emitter<GarageDoorState> emit) async {
|
||||
emit(ScheduleGarageLoadingState());
|
||||
try {
|
||||
List<ScheduleModel> schedules =
|
||||
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category);
|
||||
deviceStatus = deviceStatus.copyWith(schedules: schedules);
|
||||
emit(
|
||||
GarageDoorLoadedState(
|
||||
status: deviceStatus,
|
||||
scheduleMode: ScheduleModes.schedule,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
GarageDoorLoadedState(
|
||||
status: deviceStatus,
|
||||
scheduleMode: ScheduleModes.schedule,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateSelectedTime(UpdateSelectedTimeEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
emit(currentState.copyWith(selectedTime: event.selectedTime));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateSelectedDay(UpdateSelectedDayEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
List<bool> updatedDays = List.from(currentState.selectedDays);
|
||||
updatedDays[event.dayIndex] = event.isSelected;
|
||||
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateFunctionOn(UpdateFunctionOnEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
emit(currentState.copyWith(functionOn: event.functionOn, selectedTime: currentState.selectedTime));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializeAddSchedule(InitializeAddScheduleEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final currentState = state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
emit(currentState.copyWith(
|
||||
selectedTime: event.selectedTime,
|
||||
selectedDays: event.selectedDays,
|
||||
functionOn: event.functionOn,
|
||||
isEditing: event.isEditing,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchRecords(FetchGarageDoorRecordsEvent event, Emitter<GarageDoorState> emit) async {
|
||||
emit(GarageDoorReportsLoadingState());
|
||||
try {
|
||||
final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
|
||||
final to = DateTime.now().millisecondsSinceEpoch;
|
||||
final DeviceReport records =
|
||||
await DevicesManagementApi.getDeviceReportsByDate(event.deviceId, 'switch_1', from.toString(), to.toString());
|
||||
emit(GarageDoorReportsState(deviceReport: records));
|
||||
} catch (e) {
|
||||
emit(GarageDoorReportsFailedState(error: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onBatchControl(GarageDoorBatchControlEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final oldValue = event.code == 'switch_1' ? deviceStatus.switch1 : false;
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(GarageDoorBatchStatusLoaded(deviceStatus));
|
||||
|
||||
final success = await _runDeBouncer(
|
||||
deviceId: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
_revertValue(event.code, oldValue, emit);
|
||||
}
|
||||
}
|
||||
|
||||
void _backToGridView(BackToGarageDoorGridViewEvent event, Emitter<GarageDoorState> emit) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
|
||||
void _handleUpdate(GarageDoorUpdatedEvent event, Emitter<GarageDoorState> emit) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
|
||||
Future<bool> _runDeBouncer({
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required dynamic value,
|
||||
required dynamic oldValue,
|
||||
required Emitter<GarageDoorState> emit,
|
||||
required bool isBatch,
|
||||
}) async {
|
||||
try {
|
||||
late bool status;
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
if (isBatch) {
|
||||
status = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
|
||||
} else {
|
||||
status = await DevicesManagementApi().deviceControl(deviceId, Status(code: code, value: value));
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
_revertValue(code, oldValue, emit);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
_revertValue(code, oldValue, emit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFactoryReset(GarageDoorFactoryResetEvent event, Emitter<GarageDoorState> emit) async {
|
||||
emit(GarageDoorLoadingState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
|
||||
if (!response) {
|
||||
emit(const GarageDoorErrorState(message: 'Failed to reset device'));
|
||||
} else {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GarageDoorErrorState(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _increaseDelay(IncreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
|
||||
// if (deviceStatus.countdown1 != 0) {
|
||||
try {
|
||||
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay + Duration(minutes: 10));
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1'));
|
||||
} catch (e) {
|
||||
emit(GarageDoorErrorState(message: e.toString()));
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
void _decreaseDelay(DecreaseGarageDoorDelayEvent event, Emitter<GarageDoorState> emit) async {
|
||||
// if (deviceStatus.countdown1 != 0) {
|
||||
try {
|
||||
if (deviceStatus.delay.inMinutes > 10) {
|
||||
deviceStatus = deviceStatus.copyWith(delay: deviceStatus.delay - Duration(minutes: 10));
|
||||
}
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
add(GarageDoorControlEvent(deviceId: event.deviceId, value: deviceStatus.delay.inSeconds, code: 'countdown_1'));
|
||||
} catch (e) {
|
||||
emit(GarageDoorErrorState(message: e.toString()));
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void _garageDoorControlEvent(GarageDoorControlEvent event, Emitter<GarageDoorState> emit) async {
|
||||
final oldValue = event.code == 'countdown_1' ? deviceStatus.countdown1 : deviceStatus.switch1;
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
final success = await _runDeBouncer(
|
||||
deviceId: event.deviceId,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
if (!success) {
|
||||
_revertValue(event.code, oldValue, emit);
|
||||
}
|
||||
}
|
||||
|
||||
void _revertValue(String code, dynamic oldValue, Emitter<GarageDoorState> emit) {
|
||||
switch (code) {
|
||||
case 'switch_1':
|
||||
if (oldValue is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(switch1: oldValue);
|
||||
}
|
||||
break;
|
||||
case 'countdown_1':
|
||||
if (oldValue is int) {
|
||||
deviceStatus = deviceStatus.copyWith(countdown1: oldValue, delay: Duration(seconds: oldValue));
|
||||
}
|
||||
break;
|
||||
// Add other cases if needed
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (state is GarageDoorLoadedState) {
|
||||
final currentState = state as GarageDoorLoadedState;
|
||||
emit(currentState.copyWith(status: deviceStatus));
|
||||
}
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, dynamic value) {
|
||||
switch (code) {
|
||||
case 'switch_1':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(switch1: value);
|
||||
}
|
||||
break;
|
||||
case 'countdown_1':
|
||||
if (value is int) {
|
||||
deviceStatus = deviceStatus.copyWith(countdown1: value, delay: Duration(seconds: value));
|
||||
}
|
||||
break;
|
||||
case 'countdown_alarm':
|
||||
if (value is int) {
|
||||
deviceStatus = deviceStatus.copyWith(countdownAlarm: value);
|
||||
}
|
||||
break;
|
||||
case 'tr_timecon':
|
||||
if (value is int) {
|
||||
deviceStatus = deviceStatus.copyWith(trTimeCon: value);
|
||||
}
|
||||
break;
|
||||
case 'door_state_1':
|
||||
if (value is String) {
|
||||
deviceStatus = deviceStatus.copyWith(doorState1: value);
|
||||
}
|
||||
break;
|
||||
case 'door_control_1':
|
||||
if (value is String) {
|
||||
deviceStatus = deviceStatus.copyWith(doorControl1: value);
|
||||
}
|
||||
break;
|
||||
case 'voice_control_1':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(voiceControl1: value);
|
||||
}
|
||||
break;
|
||||
case 'door_contact_state':
|
||||
if (value is bool) {
|
||||
deviceStatus = deviceStatus.copyWith(doorContactState: value);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
|
||||
FutureOr<void> _onEditSchedule(EditGarageDoorScheduleEvent event, Emitter<GarageDoorState> emit) async {
|
||||
try {
|
||||
ScheduleEntry newSchedule = ScheduleEntry(
|
||||
scheduleId: event.scheduleId,
|
||||
category: event.category,
|
||||
time: formatTimeOfDayToISO(event.time),
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
|
||||
);
|
||||
bool success = await DevicesManagementApi().editScheduleRecord(deviceId, newSchedule);
|
||||
if (success) {
|
||||
add(FetchGarageDoorSchedulesEvent(deviceId: deviceId, category: 'switch_1'));
|
||||
} else {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(GarageDoorLoadedState(status: deviceStatus));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,234 @@
|
||||
// lib/pages/device_managment/garage_door/bloc/event.dart
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
abstract class GarageDoorEvent extends Equatable {
|
||||
const GarageDoorEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GarageDoorInitialEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
|
||||
const GarageDoorInitialEvent(this.deviceId);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId];
|
||||
}
|
||||
|
||||
class GarageDoorControlEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
final dynamic value;
|
||||
final String code;
|
||||
|
||||
const GarageDoorControlEvent({required this.deviceId, required this.value, required this.code});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId, value];
|
||||
}
|
||||
|
||||
class AddGarageDoorScheduleEvent extends GarageDoorEvent {
|
||||
final String category;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final List<bool> selectedDays;
|
||||
|
||||
const AddGarageDoorScheduleEvent({
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.selectedDays,
|
||||
});
|
||||
}
|
||||
|
||||
class EditGarageDoorScheduleEvent extends GarageDoorEvent {
|
||||
final String scheduleId;
|
||||
final String category;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final List<bool> selectedDays;
|
||||
|
||||
const EditGarageDoorScheduleEvent({
|
||||
required this.scheduleId,
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.selectedDays,
|
||||
});
|
||||
}
|
||||
|
||||
class UpdateGarageDoorScheduleEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
final String scheduleId;
|
||||
final bool enable;
|
||||
final bool functionOn;
|
||||
final int index;
|
||||
|
||||
const UpdateGarageDoorScheduleEvent({
|
||||
required this.deviceId,
|
||||
required this.scheduleId,
|
||||
required this.enable,
|
||||
required this.functionOn,
|
||||
required this.index,
|
||||
});
|
||||
}
|
||||
|
||||
class DeleteGarageDoorScheduleEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
final String scheduleId;
|
||||
final int index;
|
||||
|
||||
const DeleteGarageDoorScheduleEvent({
|
||||
required this.deviceId,
|
||||
required this.scheduleId,
|
||||
required this.index,
|
||||
});
|
||||
}
|
||||
|
||||
class FetchGarageDoorSchedulesEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
final String category;
|
||||
|
||||
const FetchGarageDoorSchedulesEvent({
|
||||
required this.deviceId,
|
||||
required this.category,
|
||||
});
|
||||
}
|
||||
|
||||
class IncreaseGarageDoorDelayEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
|
||||
const IncreaseGarageDoorDelayEvent({required this.deviceId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId];
|
||||
}
|
||||
|
||||
class DecreaseGarageDoorDelayEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
|
||||
const DecreaseGarageDoorDelayEvent({required this.deviceId});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId];
|
||||
}
|
||||
|
||||
class FetchGarageDoorRecordsEvent extends GarageDoorEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
|
||||
const FetchGarageDoorRecordsEvent({required this.deviceId, required this.code});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceId, code];
|
||||
}
|
||||
|
||||
class BackToGarageDoorGridViewEvent extends GarageDoorEvent {}
|
||||
|
||||
class GarageDoorUpdatedEvent extends GarageDoorEvent {}
|
||||
|
||||
class UpdateSelectedTimeEvent extends GarageDoorEvent {
|
||||
final TimeOfDay? selectedTime;
|
||||
|
||||
const UpdateSelectedTimeEvent(this.selectedTime);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [selectedTime];
|
||||
}
|
||||
|
||||
class UpdateSelectedDayEvent extends GarageDoorEvent {
|
||||
final int dayIndex;
|
||||
final bool isSelected;
|
||||
|
||||
const UpdateSelectedDayEvent(this.dayIndex, this.isSelected);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [dayIndex, isSelected];
|
||||
}
|
||||
|
||||
class UpdateFunctionOnEvent extends GarageDoorEvent {
|
||||
final bool functionOn;
|
||||
|
||||
const UpdateFunctionOnEvent({required this.functionOn});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [functionOn];
|
||||
}
|
||||
|
||||
class InitializeAddScheduleEvent extends GarageDoorEvent {
|
||||
final TimeOfDay? selectedTime;
|
||||
final List<bool> selectedDays;
|
||||
final bool functionOn;
|
||||
final bool isEditing;
|
||||
final int? index;
|
||||
|
||||
const InitializeAddScheduleEvent({
|
||||
required this.selectedTime,
|
||||
required this.selectedDays,
|
||||
required this.functionOn,
|
||||
required this.isEditing,
|
||||
this.index,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
selectedTime,
|
||||
selectedDays,
|
||||
functionOn,
|
||||
isEditing,
|
||||
index,
|
||||
];
|
||||
}
|
||||
|
||||
class UpdateCountdownAlarmEvent extends GarageDoorEvent {
|
||||
final int countdownAlarm;
|
||||
|
||||
const UpdateCountdownAlarmEvent(this.countdownAlarm);
|
||||
}
|
||||
|
||||
class UpdateTrTimeConEvent extends GarageDoorEvent {
|
||||
final int trTimeCon;
|
||||
|
||||
const UpdateTrTimeConEvent(this.trTimeCon);
|
||||
}
|
||||
|
||||
class GarageDoorBatchControlEvent extends GarageDoorEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
const GarageDoorBatchControlEvent({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceIds, code, value];
|
||||
}
|
||||
|
||||
class GarageDoorFetchBatchStatusEvent extends GarageDoorEvent {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const GarageDoorFetchBatchStatusEvent(this.deviceIds);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceIds];
|
||||
}
|
||||
|
||||
class GarageDoorFactoryResetEvent extends GarageDoorEvent {
|
||||
final FactoryResetModel factoryReset;
|
||||
final String deviceId;
|
||||
|
||||
const GarageDoorFactoryResetEvent({
|
||||
required this.factoryReset,
|
||||
required this.deviceId,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [factoryReset, deviceId];
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
// lib/pages/device_managment/garage_door/bloc/state.dart
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
|
||||
abstract class GarageDoorState extends Equatable {
|
||||
const GarageDoorState();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GarageDoorInitialState extends GarageDoorState {}
|
||||
|
||||
class GarageDoorLoadingState extends GarageDoorState {}
|
||||
|
||||
class GarageDoorLoadedState extends GarageDoorState {
|
||||
final GarageDoorStatusModel status;
|
||||
final Duration? delay;
|
||||
final DeviceReport? records;
|
||||
final List<bool> selectedDays;
|
||||
final TimeOfDay? selectedTime;
|
||||
final bool functionOn;
|
||||
final bool isEditing;
|
||||
final ScheduleModes? scheduleMode;
|
||||
|
||||
const GarageDoorLoadedState({
|
||||
required this.status,
|
||||
this.delay,
|
||||
this.records,
|
||||
this.selectedDays = const [false, false, false, false, false, false, false],
|
||||
this.selectedTime,
|
||||
this.functionOn = false,
|
||||
this.isEditing = false,
|
||||
this.scheduleMode = ScheduleModes.schedule,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
status,
|
||||
delay,
|
||||
records,
|
||||
selectedDays,
|
||||
selectedTime,
|
||||
functionOn,
|
||||
isEditing,
|
||||
scheduleMode,
|
||||
];
|
||||
|
||||
GarageDoorLoadedState copyWith({
|
||||
GarageDoorStatusModel? status,
|
||||
Duration? delay,
|
||||
DeviceReport? records,
|
||||
List<bool>? selectedDays,
|
||||
TimeOfDay? selectedTime,
|
||||
bool? functionOn,
|
||||
bool? isEditing,
|
||||
ScheduleModes? scheduleMode,
|
||||
}) {
|
||||
return GarageDoorLoadedState(
|
||||
status: status ?? this.status,
|
||||
delay: delay ?? this.delay,
|
||||
records: records ?? this.records,
|
||||
selectedDays: selectedDays ?? this.selectedDays,
|
||||
selectedTime: selectedTime,
|
||||
functionOn: functionOn ?? this.functionOn,
|
||||
isEditing: isEditing ?? this.isEditing,
|
||||
scheduleMode: scheduleMode ?? this.scheduleMode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GarageDoorErrorState extends GarageDoorState {
|
||||
final String message;
|
||||
|
||||
const GarageDoorErrorState({required this.message});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
class GarageDoorLoadingNewState extends GarageDoorState {
|
||||
final GarageDoorStatusModel garageDoorModel;
|
||||
|
||||
const GarageDoorLoadingNewState({required this.garageDoorModel});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [garageDoorModel];
|
||||
}
|
||||
|
||||
class GarageDoorReportsLoadingState extends GarageDoorState {}
|
||||
|
||||
class GarageDoorReportsFailedState extends GarageDoorState {
|
||||
final String error;
|
||||
|
||||
const GarageDoorReportsFailedState({required this.error});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [error];
|
||||
}
|
||||
|
||||
class GarageDoorReportsState extends GarageDoorState {
|
||||
final DeviceReport deviceReport;
|
||||
|
||||
const GarageDoorReportsState({required this.deviceReport});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [deviceReport];
|
||||
}
|
||||
|
||||
class ShowGarageDoorDescriptionState extends GarageDoorState {
|
||||
final String description;
|
||||
|
||||
const ShowGarageDoorDescriptionState({required this.description});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [description];
|
||||
}
|
||||
|
||||
class ScheduleGarageLoadingState extends GarageDoorState {}
|
||||
|
||||
class GarageDoorBatchStatusLoaded extends GarageDoorState {
|
||||
final GarageDoorStatusModel status;
|
||||
|
||||
const GarageDoorBatchStatusLoaded(this.status);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [status];
|
||||
}
|
||||
|
||||
class GarageDoorBatchControlError extends GarageDoorState {
|
||||
final String message;
|
||||
|
||||
const GarageDoorBatchControlError(this.message);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
@ -0,0 +1,385 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/opening_clsoing_time_dialog_body.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/time_out_alarm_dialog_body.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class GarageDoorDialogHelper {
|
||||
static void showAddGarageDoorScheduleDialog(BuildContext context,
|
||||
{ScheduleModel? schedule, int? index, bool? isEdit}) {
|
||||
final bloc = context.read<GarageDoorBloc>();
|
||||
|
||||
if (schedule == null) {
|
||||
bloc.add((const UpdateSelectedTimeEvent(null)));
|
||||
bloc.add(InitializeAddScheduleEvent(
|
||||
selectedTime: null,
|
||||
selectedDays: List.filled(7, false),
|
||||
functionOn: false,
|
||||
isEditing: false,
|
||||
));
|
||||
} else {
|
||||
final time = _convertStringToTimeOfDay(schedule.time);
|
||||
final selectedDays = _convertDaysStringToBooleans(schedule.days);
|
||||
|
||||
bloc.add(InitializeAddScheduleEvent(
|
||||
selectedTime: time,
|
||||
selectedDays: selectedDays,
|
||||
functionOn: schedule.function.value,
|
||||
isEditing: true,
|
||||
index: index,
|
||||
));
|
||||
}
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is GarageDoorLoadedState) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
'Scheduling',
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SizedBox(
|
||||
width: 150,
|
||||
height: 40,
|
||||
child: DefaultButton(
|
||||
padding: 8,
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
borderRadius: 15,
|
||||
onPressed: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.selectedTime ?? TimeOfDay.now(),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (time != null) {
|
||||
bloc.add(UpdateSelectedTimeEvent(time));
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
state.selectedTime == null ? 'Time' : state.selectedTime!.format(context),
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const Icon(
|
||||
Icons.access_time,
|
||||
color: ColorsManager.grayColor,
|
||||
size: 18,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDayCheckboxes(context, state.selectedDays, isEdit: isEdit),
|
||||
const SizedBox(height: 16),
|
||||
_buildFunctionSwitch(context, state.functionOn, isEdit),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
if (state.selectedTime != null) {
|
||||
if (state.isEditing && index != null) {
|
||||
bloc.add(EditGarageDoorScheduleEvent(
|
||||
scheduleId: schedule?.scheduleId ?? '',
|
||||
category: 'switch_1',
|
||||
time: state.selectedTime!,
|
||||
selectedDays: state.selectedDays,
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
} else {
|
||||
bloc.add(AddGarageDoorScheduleEvent(
|
||||
category: 'switch_1',
|
||||
time: state.selectedTime!,
|
||||
selectedDays: state.selectedDays,
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
}
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
static TimeOfDay _convertStringToTimeOfDay(String timeString) {
|
||||
final regex = RegExp(r'^(\d{2}):(\d{2})$');
|
||||
final match = regex.firstMatch(timeString);
|
||||
if (match != null) {
|
||||
final hour = int.parse(match.group(1)!);
|
||||
final minute = int.parse(match.group(2)!);
|
||||
return TimeOfDay(hour: hour, minute: minute);
|
||||
} else {
|
||||
throw const FormatException('Invalid time format');
|
||||
}
|
||||
}
|
||||
|
||||
static List<bool> _convertDaysStringToBooleans(List<String> selectedDays) {
|
||||
final daysOfWeek = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
List<bool> daysBoolean = List.filled(7, false);
|
||||
|
||||
for (int i = 0; i < daysOfWeek.length; i++) {
|
||||
if (selectedDays.contains(daysOfWeek[i])) {
|
||||
daysBoolean[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return daysBoolean;
|
||||
}
|
||||
|
||||
static Widget _buildDayCheckboxes(BuildContext context, List<bool> selectedDays, {bool? isEdit}) {
|
||||
final dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
|
||||
return Row(
|
||||
children: List.generate(7, (index) {
|
||||
return Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: selectedDays[index],
|
||||
onChanged: (bool? value) {
|
||||
context.read<GarageDoorBloc>().add(UpdateSelectedDayEvent(index, value!));
|
||||
},
|
||||
),
|
||||
Text(dayLabels[index]),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildFunctionSwitch(BuildContext context, bool isOn, bool? isEdit) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
'Function:',
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.grayColor),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: true,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
context.read<GarageDoorBloc>().add(const UpdateFunctionOnEvent(functionOn: true));
|
||||
},
|
||||
),
|
||||
const Text('On'),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: false,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
context.read<GarageDoorBloc>().add(const UpdateFunctionOnEvent(functionOn: false));
|
||||
},
|
||||
),
|
||||
const Text('Off'),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static void showPreferencesDialog(BuildContext context) {
|
||||
final bloc = context.read<GarageDoorBloc>();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is GarageDoorLoadedState) {
|
||||
return AlertDialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
|
||||
/// The dialog is closed when the user taps on the close button or when the
|
||||
/// [GarageDoorBloc] state changes.
|
||||
Text(
|
||||
'Preferences',
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 24),
|
||||
SizedBox(
|
||||
width: 190,
|
||||
height: 150,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.customAlertDialog(
|
||||
alertBody: TimeOutAlarmDialogBody(bloc),
|
||||
title: 'Time Out Alarm',
|
||||
onConfirm: () {
|
||||
final updatedState = context.read<GarageDoorBloc>().state;
|
||||
if (updatedState is GarageDoorLoadedState) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorControlEvent(
|
||||
deviceId: updatedState.status.uuid,
|
||||
code: 'countdown_alarm',
|
||||
value: updatedState.status.countdownAlarm,
|
||||
),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: ToggleWidget(
|
||||
icon: "-1",
|
||||
value: state.status.doorState1 == "close_time_alarm" ? false : true,
|
||||
code: 'door_state_1',
|
||||
deviceId: bloc.deviceId,
|
||||
label: 'Alarm when door is open',
|
||||
onChange: (value) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorControlEvent(
|
||||
deviceId: bloc.deviceId,
|
||||
code: 'door_state_1',
|
||||
value: state.status.doorState1 == "close_time_alarm"
|
||||
? "unclosed_time"
|
||||
: "close_time_alarm",
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
SizedBox(
|
||||
width: 190,
|
||||
height: 150,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.customAlertDialog(
|
||||
alertBody: OpeningAndClosingTimeDialogBody(
|
||||
bloc: bloc,
|
||||
onDurationChanged: (newDuration) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
UpdateTrTimeConEvent(newDuration),
|
||||
);
|
||||
},
|
||||
),
|
||||
title: 'Opening and Closing Time',
|
||||
onConfirm: () {
|
||||
final updatedState = context.read<GarageDoorBloc>().state;
|
||||
if (updatedState is GarageDoorLoadedState) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorControlEvent(
|
||||
deviceId: updatedState.status.uuid,
|
||||
code: 'tr_timecon',
|
||||
value: updatedState.status.trTimeCon,
|
||||
),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
});
|
||||
},
|
||||
child: PresenceDisplayValue(
|
||||
value: state.status.trTimeCon.toString(),
|
||||
postfix: 'sec',
|
||||
description: 'Opening & Closing Time',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
|
||||
class GarageDoorStatusModel {
|
||||
final String uuid;
|
||||
final bool switch1;
|
||||
final int countdown1;
|
||||
final bool doorContactState;
|
||||
final int trTimeCon;
|
||||
final int countdownAlarm;
|
||||
final String doorControl1;
|
||||
final bool voiceControl1;
|
||||
final String doorState1;
|
||||
// final bool isOpen;
|
||||
final Duration delay;
|
||||
final List<ScheduleModel>? schedules; // Add schedules field
|
||||
|
||||
GarageDoorStatusModel({
|
||||
required this.uuid,
|
||||
required this.switch1,
|
||||
required this.countdown1,
|
||||
required this.doorContactState,
|
||||
required this.trTimeCon,
|
||||
required this.countdownAlarm,
|
||||
required this.doorControl1,
|
||||
required this.voiceControl1,
|
||||
required this.doorState1,
|
||||
// required this.isOpen,
|
||||
required this.delay,
|
||||
required this.schedules, // Initialize schedules
|
||||
});
|
||||
|
||||
factory GarageDoorStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||
late bool switch1;
|
||||
late int countdown1;
|
||||
late bool doorContactState;
|
||||
late int trTimeCon;
|
||||
late int countdownAlarm;
|
||||
late String doorControl1;
|
||||
late bool voiceControl1;
|
||||
late String doorState1;
|
||||
List<ScheduleModel> schedules = []; // Initialize schedules
|
||||
|
||||
for (var status in jsonList) {
|
||||
switch (status.code) {
|
||||
case 'switch_1':
|
||||
switch1 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_1':
|
||||
countdown1 = status.value ?? 0;
|
||||
break;
|
||||
case 'doorcontact_state':
|
||||
doorContactState = status.value ?? false;
|
||||
break;
|
||||
case 'tr_timecon':
|
||||
trTimeCon = status.value ?? 0;
|
||||
break;
|
||||
case 'countdown_alarm':
|
||||
countdownAlarm = status.value ?? 0;
|
||||
break;
|
||||
case 'door_control_1':
|
||||
doorControl1 = status.value ?? 'closed';
|
||||
break;
|
||||
case 'voice_control_1':
|
||||
voiceControl1 = status.value ?? false;
|
||||
break;
|
||||
case 'door_state_1':
|
||||
doorState1 = status.value ?? 'close_time_alarm';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GarageDoorStatusModel(
|
||||
uuid: id,
|
||||
switch1: switch1,
|
||||
countdown1: countdown1,
|
||||
doorContactState: doorContactState,
|
||||
trTimeCon: trTimeCon,
|
||||
countdownAlarm: countdownAlarm,
|
||||
doorControl1: doorControl1,
|
||||
voiceControl1: voiceControl1,
|
||||
doorState1: doorState1,
|
||||
// isOpen: doorState1 == 'open' ? true : false,
|
||||
delay: Duration(seconds: countdown1),
|
||||
schedules: schedules, // Assign schedules
|
||||
);
|
||||
}
|
||||
|
||||
GarageDoorStatusModel copyWith({
|
||||
String? uuid,
|
||||
bool? switch1,
|
||||
int? countdown1,
|
||||
bool? doorContactState,
|
||||
int? trTimeCon,
|
||||
int? countdownAlarm,
|
||||
String? doorControl1,
|
||||
bool? voiceControl1,
|
||||
String? doorState1,
|
||||
// bool? isOpen,
|
||||
Duration? delay,
|
||||
List<ScheduleModel>? schedules, // Add schedules to copyWith
|
||||
}) {
|
||||
return GarageDoorStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
switch1: switch1 ?? this.switch1,
|
||||
countdown1: countdown1 ?? this.countdown1,
|
||||
doorContactState: doorContactState ?? this.doorContactState,
|
||||
trTimeCon: trTimeCon ?? this.trTimeCon,
|
||||
countdownAlarm: countdownAlarm ?? this.countdownAlarm,
|
||||
doorControl1: doorControl1 ?? this.doorControl1,
|
||||
voiceControl1: voiceControl1 ?? this.voiceControl1,
|
||||
doorState1: doorState1 ?? this.doorState1,
|
||||
// isOpen: isOpen ?? this.isOpen,
|
||||
delay: delay ?? this.delay,
|
||||
schedules: schedules ?? this.schedules, // Copy schedules
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'GarageDoorStatusModel(uuid: $uuid, switch1: $switch1, countdown1: $countdown1, doorContactState: $doorContactState, trTimeCon: $trTimeCon, countdownAlarm: $countdownAlarm, doorControl1: $doorControl1, voiceControl1: $voiceControl1, doorState1: $doorState1, delay: $delay, schedules: $schedules)';
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
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/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_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/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class GarageDoorBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const GarageDoorBatchControlView({Key? key, required this.deviceIds})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => GarageDoorBloc(deviceId: deviceIds.first)
|
||||
..add(GarageDoorFetchBatchStatusEvent(deviceIds)),
|
||||
child: BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is GarageDoorLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is GarageDoorBatchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is GarageDoorBatchControlError) {
|
||||
return Center(child: Text('Error: ${state.message}'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, GarageDoorStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceIds.first,
|
||||
label: 'Garage Door',
|
||||
icon: status.switch1 ? Assets.openedDoor : Assets.closedDoor,
|
||||
onChange: (value) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorBatchControlEvent(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorFactoryResetEvent(
|
||||
deviceId: deviceIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,206 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.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';
|
||||
|
||||
import '../../main_door_sensor/view/main_door_control_view.dart';
|
||||
|
||||
class GarageDoorControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
final String deviceId;
|
||||
|
||||
const GarageDoorControlView({required this.deviceId, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => GarageDoorBloc(deviceId: deviceId)
|
||||
..add(GarageDoorInitialEvent(deviceId)),
|
||||
child: BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is GarageDoorLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is GarageDoorReportsState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
hideValueShowDescription: true,
|
||||
garageDoorSensor: true,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context
|
||||
.read<GarageDoorBloc>()
|
||||
.add(BackToGarageDoorGridViewEvent());
|
||||
},
|
||||
);
|
||||
} else if (state is GarageDoorLoadedState) {
|
||||
return _buildControlView(context, state.status);
|
||||
} else if (state is GarageDoorErrorState) {
|
||||
return Center(child: Text('Error: ${state.message}'));
|
||||
}
|
||||
return const Center(child: Text('Unknown state'));
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildControlView(BuildContext context, GarageDoorStatusModel status) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
|
||||
return GridView(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isLarge || isExtraLarge
|
||||
? 3
|
||||
: isMedium
|
||||
? 2
|
||||
: 1,
|
||||
childAspectRatio: 1.5,
|
||||
mainAxisExtent: 140,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
children: [
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: status.switch1 ? 'Opened' : 'Closed',
|
||||
icon: status.switch1 ? Assets.openedDoor : Assets.closedDoor,
|
||||
onTap: () {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorControlEvent(
|
||||
deviceId: status.uuid,
|
||||
value: !status.switch1,
|
||||
code: 'switch_1'),
|
||||
);
|
||||
},
|
||||
status: status.switch1,
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
onTap: () {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
FetchGarageDoorSchedulesEvent(
|
||||
deviceId: deviceId, category: 'switch_1'),
|
||||
);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (ctx) => BlocProvider.value(
|
||||
value: BlocProvider.of<GarageDoorBloc>(context),
|
||||
child: BuildGarageDoorScheduleView(status: status),
|
||||
));
|
||||
},
|
||||
name: 'Scheduling',
|
||||
icon: Assets.acSchedule,
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
isFullIcon: false,
|
||||
),
|
||||
ToggleWidget(
|
||||
label: '',
|
||||
labelWidget: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context
|
||||
.read<GarageDoorBloc>()
|
||||
.add(DecreaseGarageDoorDelayEvent(deviceId: status.uuid));
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.remove,
|
||||
size: 28,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
Text(
|
||||
status.delay.inHours.toString().padLeft(2, '0'),
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'h',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
Text(
|
||||
(status.delay.inMinutes % 60).toString().padLeft(2, '0'),
|
||||
style: context.textTheme.titleLarge!.copyWith(
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'm',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context
|
||||
.read<GarageDoorBloc>()
|
||||
.add(IncreaseGarageDoorDelayEvent(deviceId: status.uuid));
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.add,
|
||||
size: 28,
|
||||
color: ColorsManager.greyColor,
|
||||
),
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
],
|
||||
),
|
||||
value: status.countdown1 != 0 ? true : false,
|
||||
code: 'countdown_1',
|
||||
deviceId: status.uuid,
|
||||
icon: Assets.doorDelay,
|
||||
onChange: (value) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
GarageDoorControlEvent(
|
||||
deviceId: status.uuid,
|
||||
value: value ? status.delay.inSeconds : 0,
|
||||
code: 'countdown_1'),
|
||||
);
|
||||
},
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Records',
|
||||
icon: Assets.records,
|
||||
onTap: () {
|
||||
context.read<GarageDoorBloc>().add(FetchGarageDoorRecordsEvent(
|
||||
code: 'switch_1', deviceId: status.uuid));
|
||||
},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Preferences',
|
||||
icon: Assets.preferences,
|
||||
onTap: () {
|
||||
GarageDoorDialogHelper.showPreferencesDialog(context);
|
||||
},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/seconds_picker.dart';
|
||||
|
||||
class OpeningAndClosingTimeDialogBody extends StatefulWidget {
|
||||
final ValueChanged<int> onDurationChanged;
|
||||
final GarageDoorBloc bloc;
|
||||
|
||||
OpeningAndClosingTimeDialogBody({
|
||||
required this.onDurationChanged,
|
||||
required this.bloc,
|
||||
});
|
||||
|
||||
@override
|
||||
_OpeningAndClosingTimeDialogBodyState createState() =>
|
||||
_OpeningAndClosingTimeDialogBodyState();
|
||||
}
|
||||
|
||||
class _OpeningAndClosingTimeDialogBodyState
|
||||
extends State<OpeningAndClosingTimeDialogBody> {
|
||||
late int durationInSeconds;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
final currentState = widget.bloc.state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
setState(() {
|
||||
durationInSeconds = currentState.status.trTimeCon;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 120,
|
||||
color: Colors.white,
|
||||
child: SecondsPicker(
|
||||
initialSeconds: durationInSeconds,
|
||||
onSecondsChanged: (newSeconds) {
|
||||
setState(() {
|
||||
durationInSeconds = newSeconds;
|
||||
});
|
||||
widget.onDurationChanged(newSeconds);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
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/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.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/format_date_time.dart';
|
||||
|
||||
class ScheduleGarageTableWidget extends StatelessWidget {
|
||||
final GarageDoorLoadedState state;
|
||||
|
||||
const ScheduleGarageTableWidget({
|
||||
super.key,
|
||||
required this.state,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Table(
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.graysColor,
|
||||
borderRadius: const BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
),
|
||||
children: [
|
||||
TableRow(
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
children: [
|
||||
_buildTableHeader('Active'),
|
||||
_buildTableHeader('Days'),
|
||||
_buildTableHeader('Time'),
|
||||
_buildTableHeader('Function'),
|
||||
_buildTableHeader('Action'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is ScheduleGarageLoadingState) {
|
||||
return const SizedBox(height: 200, child: Center(child: CircularProgressIndicator()));
|
||||
}
|
||||
if (state is GarageDoorLoadedState && state.status.schedules?.isEmpty == true) {
|
||||
return _buildEmptyState(context);
|
||||
} else if (state is GarageDoorLoadedState) {
|
||||
return Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: ColorsManager.graysColor),
|
||||
borderRadius:
|
||||
const BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||
),
|
||||
child: _buildTableBody(state, context));
|
||||
}
|
||||
return const SizedBox(
|
||||
height: 200,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState(BuildContext context) {
|
||||
return Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: ColorsManager.graysColor),
|
||||
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SvgPicture.asset(Assets.emptyRecords, width: 40, height: 40),
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
'No schedules added yet',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableBody(GarageDoorLoadedState state, BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: SingleChildScrollView(
|
||||
child: Table(
|
||||
border: TableBorder.all(color: ColorsManager.graysColor),
|
||||
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
children: [
|
||||
if (state.status.schedules != null)
|
||||
for (int i = 0; i < state.status.schedules!.length; i++)
|
||||
_buildScheduleRow(state.status.schedules![i], i, context, state),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTableHeader(String label) {
|
||||
return TableCell(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TableRow _buildScheduleRow(ScheduleModel schedule, int index, BuildContext context, GarageDoorLoadedState state) {
|
||||
return TableRow(
|
||||
children: [
|
||||
Center(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.read<GarageDoorBloc>().add(UpdateGarageDoorScheduleEvent(
|
||||
index: index,
|
||||
enable: !schedule.enable,
|
||||
scheduleId: schedule.scheduleId,
|
||||
deviceId: state.status.uuid,
|
||||
functionOn: schedule.function.value,
|
||||
));
|
||||
},
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: schedule.enable
|
||||
? const Icon(Icons.radio_button_checked, color: ColorsManager.blueColor)
|
||||
: const Icon(
|
||||
Icons.radio_button_unchecked,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(child: Text(_getSelectedDays(ScheduleModel.parseSelectedDays(schedule.days)))),
|
||||
Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
||||
Center(child: Text(schedule.function.value ? 'On' : 'Off')),
|
||||
Center(
|
||||
child: Wrap(
|
||||
runAlignment: WrapAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
GarageDoorDialogHelper.showAddGarageDoorScheduleDialog(context,
|
||||
schedule: schedule, index: index, isEdit: true);
|
||||
},
|
||||
child: Text(
|
||||
'Edit',
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
onPressed: () {
|
||||
context.read<GarageDoorBloc>().add(DeleteGarageDoorScheduleEvent(
|
||||
index: index,
|
||||
scheduleId: schedule.scheduleId,
|
||||
deviceId: state.status.uuid,
|
||||
));
|
||||
},
|
||||
child: Text(
|
||||
'Delete',
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blueColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getSelectedDays(List<bool> selectedDays) {
|
||||
final days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
List<String> selectedDaysStr = [];
|
||||
for (int i = 0; i < selectedDays.length; i++) {
|
||||
if (selectedDays[i]) {
|
||||
selectedDaysStr.add(days[i]);
|
||||
}
|
||||
}
|
||||
return selectedDaysStr.join(', ');
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ScheduleGarageHeader extends StatelessWidget {
|
||||
const ScheduleGarageHeader({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
'Scheduling',
|
||||
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: const EdgeInsets.all(1),
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.grey,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule__garage_table.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ScheduleGarageManagementUI extends StatelessWidget {
|
||||
final GarageDoorLoadedState state;
|
||||
final Function onAddSchedule;
|
||||
|
||||
const ScheduleGarageManagementUI({
|
||||
super.key,
|
||||
required this.state,
|
||||
required this.onAddSchedule,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 40,
|
||||
child: DefaultButton(
|
||||
borderColor: ColorsManager.boxColor,
|
||||
padding: 2,
|
||||
backgroundColor: ColorsManager.graysColor,
|
||||
borderRadius: 15,
|
||||
onPressed: () => onAddSchedule(),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.add, color: ColorsManager.primaryColor),
|
||||
Text(
|
||||
' Add new schedule',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ScheduleGarageTableWidget(state: state),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ScheduleGarageModeButtons extends StatelessWidget {
|
||||
final VoidCallback onSave;
|
||||
|
||||
const ScheduleGarageModeButtons({
|
||||
super.key,
|
||||
required this.onSave,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
child: Text(
|
||||
'Cancel',
|
||||
style: context.textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
onPressed: onSave,
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/water_heater_status_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
class ScheduleGarageDoorModeSelector extends StatelessWidget {
|
||||
final GarageDoorLoadedState state;
|
||||
|
||||
const ScheduleGarageDoorModeSelector({super.key, required this.state});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Type:',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
_buildRadioTile(context, 'Schedule', ScheduleModes.schedule, state),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRadioTile(BuildContext context, String label, ScheduleModes mode, GarageDoorLoadedState state) {
|
||||
return Flexible(
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
label,
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
fontSize: 13,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
leading: Radio<ScheduleModes>(
|
||||
value: mode,
|
||||
groupValue: state.scheduleMode,
|
||||
onChanged: (ScheduleModes? value) {
|
||||
if (value != null) {
|
||||
if (value == ScheduleModes.schedule) {
|
||||
context.read<GarageDoorBloc>().add(
|
||||
FetchGarageDoorSchedulesEvent(
|
||||
category: 'switch_1',
|
||||
deviceId: state.status.uuid,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/helper/garage_door_helper.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/models/garage_door_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_header.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_managment_ui.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/widgets/schedule_garage_mode_buttons.dart';
|
||||
|
||||
class BuildGarageDoorScheduleView extends StatefulWidget {
|
||||
const BuildGarageDoorScheduleView({super.key, required this.status});
|
||||
|
||||
final GarageDoorStatusModel status;
|
||||
|
||||
@override
|
||||
State<BuildGarageDoorScheduleView> createState() => _BuildScheduleViewState();
|
||||
}
|
||||
|
||||
class _BuildScheduleViewState extends State<BuildGarageDoorScheduleView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bloc = BlocProvider.of<GarageDoorBloc>(context);
|
||||
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
insetPadding: const EdgeInsets.all(20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 700,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 40.0, vertical: 20),
|
||||
child: BlocBuilder<GarageDoorBloc, GarageDoorState>(
|
||||
builder: (context, state) {
|
||||
if (state is GarageDoorLoadedState) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ScheduleGarageHeader(),
|
||||
const SizedBox(height: 20),
|
||||
ScheduleGarageManagementUI(
|
||||
state: state,
|
||||
onAddSchedule: () {
|
||||
GarageDoorDialogHelper
|
||||
.showAddGarageDoorScheduleDialog(context,
|
||||
schedule: null, index: null, isEdit: false);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
ScheduleGarageModeButtons(
|
||||
onSave: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
if (state is ScheduleGarageLoadingState) {
|
||||
return SizedBox(
|
||||
height: 200,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const ScheduleGarageHeader(),
|
||||
const SizedBox(
|
||||
height: 50,
|
||||
),
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
ScheduleGarageModeButtons(
|
||||
onSave: () {},
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
return const SizedBox(
|
||||
height: 200,
|
||||
child: ScheduleGarageHeader(),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SecondsPicker extends StatefulWidget {
|
||||
final int initialSeconds;
|
||||
final ValueChanged<int> onSecondsChanged;
|
||||
|
||||
SecondsPicker({
|
||||
required this.initialSeconds,
|
||||
required this.onSecondsChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
_SecondsPickerState createState() => _SecondsPickerState();
|
||||
}
|
||||
|
||||
class _SecondsPickerState extends State<SecondsPicker> {
|
||||
late FixedExtentScrollController _scrollController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = FixedExtentScrollController(
|
||||
initialItem: widget.initialSeconds,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 120,
|
||||
color: Colors.white,
|
||||
child: ListWheelScrollView.useDelegate(
|
||||
controller: _scrollController,
|
||||
itemExtent: 48,
|
||||
onSelectedItemChanged: (index) {
|
||||
widget.onSecondsChanged(index);
|
||||
},
|
||||
physics: const FixedExtentScrollPhysics(),
|
||||
childDelegate: ListWheelChildBuilderDelegate(
|
||||
builder: (context, index) {
|
||||
return Center(
|
||||
child: Text(
|
||||
'$index sec',
|
||||
style: const TextStyle(fontSize: 24),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/garage_door/bloc/garage_door_state.dart';
|
||||
|
||||
class TimeOutAlarmDialogBody extends StatefulWidget {
|
||||
TimeOutAlarmDialogBody(this.bloc);
|
||||
final GarageDoorBloc bloc;
|
||||
|
||||
@override
|
||||
_TimeOutAlarmDialogBodyState createState() => _TimeOutAlarmDialogBodyState();
|
||||
}
|
||||
|
||||
class _TimeOutAlarmDialogBodyState extends State<TimeOutAlarmDialogBody> {
|
||||
int durationInSeconds = 0;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
|
||||
final currentState = widget.bloc.state;
|
||||
if (currentState is GarageDoorLoadedState) {
|
||||
if (currentState.status.countdownAlarm != 0) {
|
||||
setState(() {
|
||||
durationInSeconds = currentState.status.countdownAlarm;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: 120,
|
||||
color: Colors.white,
|
||||
child: CupertinoTimerPicker(
|
||||
itemExtent: 120,
|
||||
mode: CupertinoTimerPickerMode.hm,
|
||||
initialTimerDuration: Duration(seconds: durationInSeconds),
|
||||
onTimerDurationChanged: (newDuration) {
|
||||
widget.bloc.add(
|
||||
UpdateCountdownAlarmEvent(newDuration.inSeconds),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -16,12 +16,10 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
||||
on<GateWayFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
FutureOr<void> _getGatWayById(
|
||||
GatWayById event, Emitter<GateWayState> emit) async {
|
||||
FutureOr<void> _getGatWayById(GatWayById event, Emitter<GateWayState> emit) async {
|
||||
emit(GatewayLoadingState());
|
||||
try {
|
||||
List<DeviceModel> devicesList =
|
||||
await DevicesManagementApi.getDevicesByGatewayId(event.getWayId);
|
||||
List<DeviceModel> devicesList = await DevicesManagementApi.getDevicesByGatewayId(event.getWayId);
|
||||
|
||||
emit(UpdateGatewayState(list: devicesList));
|
||||
} catch (e) {
|
||||
@ -30,8 +28,7 @@ class GateWayBloc extends Bloc<GateWayEvent, GateWayState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onFactoryReset(
|
||||
GateWayFactoryReset event, Emitter<GateWayState> emit) async {
|
||||
FutureOr<void> _onFactoryReset(GateWayFactoryReset event, Emitter<GateWayState> emit) async {
|
||||
emit(GatewayLoadingState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
|
@ -74,7 +74,7 @@ class _DeviceItem extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(8),
|
||||
color: ColorsManager.whiteColors,
|
||||
child: SvgPicture.asset(
|
||||
device.icon,
|
||||
device.icon ?? 'assets/icons/gateway.svg',
|
||||
width: 35,
|
||||
height: 35,
|
||||
fit: BoxFit.contain,
|
||||
|
@ -14,8 +14,7 @@ import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class MainDoorSensorControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
class MainDoorSensorControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const MainDoorSensorControlView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
@ -23,12 +22,10 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => MainDoorSensorBloc()
|
||||
..add(MainDoorSensorFetchDeviceEvent(device.uuid!)),
|
||||
create: (context) => MainDoorSensorBloc()..add(MainDoorSensorFetchDeviceEvent(device.uuid!)),
|
||||
child: BlocBuilder<MainDoorSensorBloc, MainDoorSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is MainDoorSensorLoadingState ||
|
||||
state is MainDoorSensorReportsLoadingState) {
|
||||
if (state is MainDoorSensorLoadingState || state is MainDoorSensorReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is MainDoorSensorDeviceStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
@ -37,15 +34,12 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
report: state.deviceReport,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context
|
||||
.read<MainDoorSensorBloc>()
|
||||
.add(MainDoorSensorFetchDeviceEvent(device.uuid!));
|
||||
context.read<MainDoorSensorBloc>().add(MainDoorSensorFetchDeviceEvent(device.uuid!));
|
||||
},
|
||||
hideValueShowDescription: true,
|
||||
mainDoorSensor: true,
|
||||
);
|
||||
} else if (state is MainDoorSensorFailedState ||
|
||||
state is MainDoorSensorBatchFailedState) {
|
||||
} else if (state is MainDoorSensorFailedState || state is MainDoorSensorBatchFailedState) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
@ -54,8 +48,7 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, MainDoorSensorStatusModel status) {
|
||||
Widget _buildStatusControls(BuildContext context, MainDoorSensorStatusModel status) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
@ -80,9 +73,7 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
icon: Assets.openCloseDoor,
|
||||
onTap: () {},
|
||||
status: status.doorContactState,
|
||||
textColor: status.doorContactState
|
||||
? ColorsManager.red
|
||||
: ColorsManager.blackColor,
|
||||
textColor: status.doorContactState ? ColorsManager.red : ColorsManager.blackColor,
|
||||
paddingAmount: 8,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
@ -90,9 +81,7 @@ class MainDoorSensorControlView extends StatelessWidget
|
||||
name: 'Open/Close\nRecord',
|
||||
icon: Assets.openCloseRecords,
|
||||
onTap: () {
|
||||
final from = DateTime.now()
|
||||
.subtract(const Duration(days: 30))
|
||||
.millisecondsSinceEpoch;
|
||||
final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
|
||||
final to = DateTime.now().millisecondsSinceEpoch;
|
||||
context.read<MainDoorSensorBloc>().add(
|
||||
MainDoorSensorReportsEvent(
|
||||
@ -161,22 +150,19 @@ class IconNameStatusContainer extends StatelessWidget {
|
||||
),
|
||||
)
|
||||
else
|
||||
Container(
|
||||
width: 60,
|
||||
ClipOval(
|
||||
child: Container(
|
||||
height: 60,
|
||||
decoration: const BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: ColorsManager.whiteColors,
|
||||
width: 60,
|
||||
padding: EdgeInsets.all(paddingAmount ?? 8),
|
||||
color: ColorsManager.whiteColors,
|
||||
child: SvgPicture.asset(
|
||||
icon,
|
||||
width: 35,
|
||||
height: 35,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
//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),
|
||||
|
@ -2,9 +2,18 @@ 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 {
|
||||
class NotificationDialog extends StatefulWidget {
|
||||
const NotificationDialog({super.key});
|
||||
|
||||
@override
|
||||
State<NotificationDialog> createState() => _NotificationDialogState();
|
||||
}
|
||||
|
||||
class _NotificationDialogState extends State<NotificationDialog> {
|
||||
bool isLowBatteryNotificationEnabled = true;
|
||||
bool isClosingRemindersEnabled = true;
|
||||
bool isDoorAlarmEnabled = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
@ -14,7 +23,7 @@ class NotificationDialog extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 798,
|
||||
width: 660,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
@ -61,29 +70,53 @@ class NotificationDialog extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ToggleWidget(
|
||||
value: true,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Low Battery',
|
||||
onChange: (v) {},
|
||||
icon: '-1',
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 135,
|
||||
child: ToggleWidget(
|
||||
value: isLowBatteryNotificationEnabled,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Low Battery',
|
||||
onChange: (v) {
|
||||
setState(() {
|
||||
isLowBatteryNotificationEnabled = v;
|
||||
});
|
||||
},
|
||||
icon: '-1',
|
||||
),
|
||||
),
|
||||
ToggleWidget(
|
||||
value: true,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Closing\nReminders',
|
||||
onChange: (v) {},
|
||||
icon: '-1',
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 135,
|
||||
child: ToggleWidget(
|
||||
value: isClosingRemindersEnabled,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Closing\nReminders',
|
||||
onChange: (v) {
|
||||
setState(() {
|
||||
isClosingRemindersEnabled = v;
|
||||
});
|
||||
},
|
||||
icon: '-1',
|
||||
),
|
||||
),
|
||||
ToggleWidget(
|
||||
value: true,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Door Alarm',
|
||||
onChange: (v) {},
|
||||
icon: '-1',
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 135,
|
||||
child: ToggleWidget(
|
||||
value: isDoorAlarmEnabled,
|
||||
code: 'notification',
|
||||
deviceId: '',
|
||||
label: 'Door Alarm',
|
||||
onChange: (v) {
|
||||
setState(() {
|
||||
isDoorAlarmEnabled = v;
|
||||
});
|
||||
},
|
||||
icon: '-1',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -0,0 +1,159 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
part 'one_gang_glass_switch_event.dart';
|
||||
part 'one_gang_glass_switch_state.dart';
|
||||
|
||||
class OneGangGlassSwitchBloc extends Bloc<OneGangGlassSwitchEvent, OneGangGlassSwitchState> {
|
||||
OneGangGlassStatusModel deviceStatus;
|
||||
Timer? _timer;
|
||||
|
||||
OneGangGlassSwitchBloc({required String deviceId})
|
||||
: deviceStatus = OneGangGlassStatusModel(uuid: deviceId, switch1: false, countDown: 0),
|
||||
super(OneGangGlassSwitchInitial()) {
|
||||
on<OneGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||
on<OneGangGlassSwitchControl>(_onControl);
|
||||
on<OneGangGlassSwitchBatchControl>(_onBatchControl);
|
||||
on<OneGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
|
||||
on<OneGangGlassFactoryResetEvent>(_onFactoryReset);
|
||||
}
|
||||
|
||||
Future<void> _onFetchDeviceStatus(
|
||||
OneGangGlassSwitchFetchDeviceEvent event, Emitter<OneGangGlassSwitchState> emit) async {
|
||||
emit(OneGangGlassSwitchLoading());
|
||||
try {
|
||||
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
|
||||
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(OneGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onControl(OneGangGlassSwitchControl event, Emitter<OneGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceId,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFactoryReset(OneGangGlassFactoryResetEvent event, Emitter<OneGangGlassSwitchState> emit) async {
|
||||
emit(OneGangGlassSwitchLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
|
||||
if (!response) {
|
||||
emit(OneGangGlassSwitchError('Failed to reset device'));
|
||||
} else {
|
||||
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(OneGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onBatchControl(OneGangGlassSwitchBatchControl event, Emitter<OneGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFetchBatchStatus(
|
||||
OneGangGlassSwitchFetchBatchStatusEvent event, Emitter<OneGangGlassSwitchState> emit) async {
|
||||
emit(OneGangGlassSwitchLoading());
|
||||
try {
|
||||
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
|
||||
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceIds.first, status.status);
|
||||
emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(OneGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _runDebounce({
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required bool value,
|
||||
required bool oldValue,
|
||||
required Emitter<OneGangGlassSwitchState> 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<OneGangGlassSwitchState> emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
emit(OneGangGlassSwitchStatusLoaded(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;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
part of 'one_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class OneGangGlassSwitchEvent {}
|
||||
|
||||
class OneGangGlassSwitchFetchDeviceEvent extends OneGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
|
||||
OneGangGlassSwitchFetchDeviceEvent(this.deviceId);
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchControl extends OneGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
OneGangGlassSwitchControl({
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchBatchControl extends OneGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
OneGangGlassSwitchBatchControl({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchFetchBatchStatusEvent extends OneGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
|
||||
OneGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
|
||||
}
|
||||
|
||||
class OneGangGlassFactoryResetEvent extends OneGangGlassSwitchEvent {
|
||||
final FactoryResetModel factoryReset;
|
||||
final String deviceId;
|
||||
|
||||
OneGangGlassFactoryResetEvent({
|
||||
required this.factoryReset,
|
||||
required this.deviceId,
|
||||
});
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
part of 'one_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class OneGangGlassSwitchState {}
|
||||
|
||||
class OneGangGlassSwitchInitial extends OneGangGlassSwitchState {}
|
||||
|
||||
class OneGangGlassSwitchLoading extends OneGangGlassSwitchState {}
|
||||
|
||||
class OneGangGlassSwitchStatusLoaded extends OneGangGlassSwitchState {
|
||||
final OneGangGlassStatusModel status;
|
||||
|
||||
OneGangGlassSwitchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchError extends OneGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
OneGangGlassSwitchError(this.message);
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchBatchStatusLoaded extends OneGangGlassSwitchState {
|
||||
final OneGangGlassStatusModel status;
|
||||
|
||||
OneGangGlassSwitchBatchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class OneGangGlassSwitchBatchControlError extends OneGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
OneGangGlassSwitchBatchControlError(this.message);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class OneGangGlassStatusModel {
|
||||
final String uuid;
|
||||
final bool switch1;
|
||||
final int countDown;
|
||||
|
||||
OneGangGlassStatusModel({
|
||||
required this.uuid,
|
||||
required this.switch1,
|
||||
required this.countDown,
|
||||
});
|
||||
|
||||
factory OneGangGlassStatusModel.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 OneGangGlassStatusModel(
|
||||
uuid: id,
|
||||
switch1: switch1,
|
||||
countDown: countDown,
|
||||
);
|
||||
}
|
||||
|
||||
OneGangGlassStatusModel copyWith({
|
||||
String? uuid,
|
||||
bool? switch1,
|
||||
int? countDown,
|
||||
}) {
|
||||
return OneGangGlassStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
switch1: switch1 ?? this.switch1,
|
||||
countDown: countDown ?? this.countDown,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'OneGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown: $countDown)';
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
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_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_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 OneGangGlassSwitchBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const OneGangGlassSwitchBatchControlView(
|
||||
{required this.deviceIds, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => OneGangGlassSwitchBloc(deviceId: deviceIds.first)
|
||||
..add(OneGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
|
||||
child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is OneGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is OneGangGlassSwitchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is OneGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, OneGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceIds.first,
|
||||
label: 'Wall Light',
|
||||
onChange: (value) {
|
||||
context.read<OneGangGlassSwitchBloc>().add(
|
||||
OneGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(
|
||||
deviceId: deviceIds.first,
|
||||
version: 12,
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<OneGangGlassSwitchBloc>().add(
|
||||
OneGangGlassFactoryResetEvent(
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||
deviceId: deviceIds.first,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/bloc/one_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/one_g_glass_switch/models/once_gang_glass_status_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/toggle_widget.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class OneGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final String deviceId;
|
||||
|
||||
const OneGangGlassSwitchControlView({required this.deviceId, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
OneGangGlassSwitchBloc(deviceId: deviceId)..add(OneGangGlassSwitchFetchDeviceEvent(deviceId)),
|
||||
child: BlocBuilder<OneGangGlassSwitchBloc, OneGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is OneGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is OneGangGlassSwitchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is OneGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, OneGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceId,
|
||||
label: "Wall Light",
|
||||
onChange: (value) {
|
||||
context.read<OneGangGlassSwitchBloc>().add(
|
||||
OneGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Preferences',
|
||||
icon: Assets.preferences,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Scheduling',
|
||||
icon: Assets.scheduling,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,43 +1,128 @@
|
||||
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 FirmwareUpdateWidget extends StatelessWidget {
|
||||
const FirmwareUpdateWidget(
|
||||
{super.key, required String deviceId, required int version});
|
||||
class FirmwareUpdateWidget extends StatefulWidget {
|
||||
const FirmwareUpdateWidget({super.key, required this.deviceId, required this.version});
|
||||
|
||||
final String deviceId;
|
||||
final int version;
|
||||
|
||||
@override
|
||||
State<FirmwareUpdateWidget> createState() => _FirmwareUpdateWidgetState();
|
||||
}
|
||||
|
||||
class _FirmwareUpdateWidgetState extends State<FirmwareUpdateWidget> {
|
||||
bool _showConfirmation = false;
|
||||
|
||||
void _toggleConfirmation() {
|
||||
setState(() {
|
||||
_showConfirmation = !_showConfirmation;
|
||||
});
|
||||
}
|
||||
|
||||
@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,
|
||||
child: _showConfirmation
|
||||
? Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'Firmware Update',
|
||||
style: context.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Are you sure?',
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: DefaultButton(
|
||||
height: 20,
|
||||
elevation: 0,
|
||||
padding: 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,
|
||||
padding: 0,
|
||||
onPressed: () {
|
||||
_toggleConfirmation();
|
||||
},
|
||||
backgroundColor: ColorsManager.primaryColor,
|
||||
child: Text(
|
||||
'Update',
|
||||
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.firmware,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Firmware Update',
|
||||
style: context.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)),
|
||||
Text(
|
||||
'Firmware Update',
|
||||
style: context.textTheme.titleMedium!.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,7 @@ import 'package:syncrow_web/pages/device_managment/all_devices/helper/route_cont
|
||||
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 {
|
||||
class DeviceBatchControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||
final List<AllDevicesModel> devices;
|
||||
|
||||
const DeviceBatchControlDialog({super.key, required this.devices});
|
||||
@ -110,11 +109,11 @@ String getBatchDialogName(AllDevicesModel device) {
|
||||
case '1G':
|
||||
return "Smart Light Switch";
|
||||
case '2G':
|
||||
return "2Gang Light";
|
||||
return "Smart Light Switch";
|
||||
case '3G':
|
||||
return "Living Room";
|
||||
return "Smart Light Switch";
|
||||
case 'GW':
|
||||
return "GateWay";
|
||||
return "Gateway";
|
||||
case 'DL':
|
||||
return "Door Lock";
|
||||
case 'WPS':
|
||||
@ -124,9 +123,21 @@ String getBatchDialogName(AllDevicesModel device) {
|
||||
case 'CUR':
|
||||
return "Smart Curtains";
|
||||
case 'WH':
|
||||
return "Smart Water Hater";
|
||||
return "Smart Water Heater";
|
||||
case 'AC':
|
||||
return "Smart AC";
|
||||
case 'DS':
|
||||
return "Door / Window Sensor";
|
||||
case '1GT':
|
||||
return "Touch Switch";
|
||||
case '2GT':
|
||||
return "Touch Switch";
|
||||
case '3GT':
|
||||
return "Touch Switch";
|
||||
case 'GD':
|
||||
return "Garage Door Opener";
|
||||
case 'WL':
|
||||
return "Water Leak Sensor";
|
||||
default:
|
||||
return device.categoryName ?? 'Device Control';
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.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';
|
||||
import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
@ -22,7 +19,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||
),
|
||||
child: SizedBox(
|
||||
width: 798,
|
||||
// height: context.screenHeight * 0.7,
|
||||
//height: context.screenHeight * 0.7,
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
@ -34,7 +31,7 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
device.categoryName ?? 'Device Control',
|
||||
device.productName ?? 'Device Control',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 22,
|
||||
@ -113,13 +110,9 @@ class DeviceControlDialog extends StatelessWidget with RouteControlsBasedCode {
|
||||
),
|
||||
_buildInfoRow(
|
||||
'Battery Level:',
|
||||
device.batteryLevel != null
|
||||
? '${device.batteryLevel ?? 0}%'
|
||||
: "-",
|
||||
device.batteryLevel != null ? '${device.batteryLevel ?? 0}%' : "-",
|
||||
statusColor: device.batteryLevel != null
|
||||
? (device.batteryLevel! < 20
|
||||
? ColorsManager.red
|
||||
: ColorsManager.green)
|
||||
? (device.batteryLevel! < 20 ? ColorsManager.red : ColorsManager.green)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
|
@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class DeviceControlsContainer extends StatelessWidget {
|
||||
const DeviceControlsContainer({required this.child, super.key});
|
||||
const DeviceControlsContainer({required this.child, this.padding, super.key});
|
||||
final Widget child;
|
||||
final double? padding;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -11,9 +12,16 @@ class DeviceControlsContainer extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
color: ColorsManager.greyColor.withOpacity(0.2),
|
||||
border: Border.all(color: ColorsManager.boxDivider),
|
||||
|
||||
// boxShadow: <BoxShadow>[
|
||||
// BoxShadow(
|
||||
// color: ColorsManager.blackColor.withOpacity(0.05),
|
||||
// blurRadius: 6.0,
|
||||
// offset: const Offset(0, 5),
|
||||
// spreadRadius: 0)
|
||||
// ],
|
||||
),
|
||||
padding: const EdgeInsets.all(12),
|
||||
padding: EdgeInsets.all(padding ?? 12),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import 'package:intl/intl.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.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/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class ReportsTable extends StatelessWidget {
|
||||
@ -13,6 +15,8 @@ class ReportsTable extends StatelessWidget {
|
||||
final VoidCallback onClose;
|
||||
bool? hideValueShowDescription;
|
||||
bool? mainDoorSensor;
|
||||
bool? garageDoorSensor;
|
||||
bool? waterLeak;
|
||||
|
||||
ReportsTable({
|
||||
super.key,
|
||||
@ -23,79 +27,96 @@ class ReportsTable extends StatelessWidget {
|
||||
this.thirdColumnDescription,
|
||||
this.hideValueShowDescription,
|
||||
this.mainDoorSensor,
|
||||
this.garageDoorSensor,
|
||||
this.waterLeak,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Table(
|
||||
border: TableBorder.all(color: Colors.grey.shade300, width: 1),
|
||||
columnWidths: const {
|
||||
0: FlexColumnWidth(),
|
||||
1: FlexColumnWidth(),
|
||||
2: FlexColumnWidth(),
|
||||
},
|
||||
return report.data == null || report.data!.isEmpty
|
||||
? Container(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
width: MediaQuery.sizeOf(context).width,
|
||||
alignment: AlignmentDirectional.center,
|
||||
height: 100,
|
||||
child: Text(
|
||||
'No reports found',
|
||||
style: context.textTheme.bodyLarge!.copyWith(color: ColorsManager.grayColor),
|
||||
),
|
||||
)
|
||||
: Stack(
|
||||
children: [
|
||||
TableRow(
|
||||
decoration: BoxDecoration(color: Colors.grey.shade200),
|
||||
children: [
|
||||
const TableHeader(title: 'Date'),
|
||||
const TableHeader(title: 'Time'),
|
||||
TableHeader(title: thirdColumnTitle ?? 'Status'),
|
||||
],
|
||||
),
|
||||
if (report.data != null)
|
||||
...report.data!.asMap().entries.map((entry) {
|
||||
int index = entry.key;
|
||||
DeviceEvent data = entry.value;
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Table(
|
||||
border: TableBorder.all(color: Colors.grey.shade300, width: 1),
|
||||
columnWidths: const {
|
||||
0: FlexColumnWidth(),
|
||||
1: FlexColumnWidth(),
|
||||
2: FlexColumnWidth(),
|
||||
},
|
||||
children: [
|
||||
TableRow(
|
||||
decoration: BoxDecoration(color: Colors.grey.shade200),
|
||||
children: [
|
||||
const TableHeader(title: 'Date'),
|
||||
const TableHeader(title: 'Time'),
|
||||
TableHeader(title: thirdColumnTitle ?? 'Status'),
|
||||
],
|
||||
),
|
||||
if (report.data != null)
|
||||
...report.data!.asMap().entries.map((entry) {
|
||||
int index = entry.key;
|
||||
DeviceEvent data = entry.value;
|
||||
|
||||
// Parse eventTime into Date and Time
|
||||
DateTime eventDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
|
||||
String date = DateFormat('dd/MM/yyyy').format(eventDateTime);
|
||||
String time = DateFormat('HH:mm').format(eventDateTime);
|
||||
// Parse eventTime into Date and Time
|
||||
DateTime eventDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(data.eventTime!);
|
||||
String date = DateFormat('dd/MM/yyyy').format(eventDateTime);
|
||||
String time = DateFormat('HH:mm').format(eventDateTime);
|
||||
|
||||
return TableRow(
|
||||
children: [
|
||||
TableCellWidget(value: date),
|
||||
TableCellWidget(value: time),
|
||||
hideValueShowDescription == true
|
||||
? TableCellWidget(
|
||||
value: (mainDoorSensor != null &&
|
||||
mainDoorSensor == true)
|
||||
? data.value == 'true'
|
||||
? 'Open'
|
||||
: 'Close'
|
||||
: thirdColumnDescription ?? '',
|
||||
onTap: () => onRowTap(index),
|
||||
)
|
||||
: TableCellWidget(
|
||||
value:
|
||||
'${data.value!} ${thirdColumnDescription ?? ''}',
|
||||
String value;
|
||||
if (hideValueShowDescription == true) {
|
||||
if (mainDoorSensor != null && mainDoorSensor == true) {
|
||||
value = data.value == 'true' ? 'Open' : 'Close';
|
||||
} else if (garageDoorSensor != null && garageDoorSensor == true) {
|
||||
value = data.value == 'true' ? 'Opened' : 'Closed';
|
||||
} else if (waterLeak != null && waterLeak == true) {
|
||||
value = data.value == 'normal' ? 'Normal' : 'Leak Detected';
|
||||
} else {
|
||||
value = '${data.value!} ${thirdColumnDescription ?? ''}';
|
||||
}
|
||||
} else {
|
||||
value = '${data.value!} ${thirdColumnDescription ?? ''}';
|
||||
}
|
||||
|
||||
return TableRow(
|
||||
children: [
|
||||
TableCellWidget(value: date),
|
||||
TableCellWidget(value: time),
|
||||
TableCellWidget(
|
||||
value: value,
|
||||
onTap: () => onRowTap(index),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.red,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: onClose,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 0,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: Colors.red,
|
||||
size: 18,
|
||||
),
|
||||
onPressed: onClose,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
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';
|
||||
@ -13,6 +13,8 @@ class ToggleWidget extends StatelessWidget {
|
||||
final String? icon;
|
||||
final Widget? labelWidget;
|
||||
final Function(dynamic value) onChange;
|
||||
final bool showToggle;
|
||||
final bool showIcon;
|
||||
|
||||
const ToggleWidget({
|
||||
super.key,
|
||||
@ -23,17 +25,13 @@ class ToggleWidget extends StatelessWidget {
|
||||
required this.onChange,
|
||||
this.icon,
|
||||
this.labelWidget,
|
||||
this.showToggle = true,
|
||||
this.showIcon = true,
|
||||
});
|
||||
|
||||
@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),
|
||||
return DeviceControlsContainer(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -62,16 +60,17 @@ class ToggleWidget extends StatelessWidget {
|
||||
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,
|
||||
if (showToggle)
|
||||
Container(
|
||||
height: 20,
|
||||
width: 35,
|
||||
padding: const EdgeInsets.only(right: 16, top: 10),
|
||||
child: CupertinoSwitch(
|
||||
value: value,
|
||||
activeColor: ColorsManager.dialogBlueTitle,
|
||||
onChanged: onChange,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -0,0 +1,174 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
part 'three_gang_glass_switch_event.dart';
|
||||
part 'three_gang_glass_switch_state.dart';
|
||||
|
||||
class ThreeGangGlassSwitchBloc extends Bloc<ThreeGangGlassSwitchEvent, ThreeGangGlassSwitchState> {
|
||||
ThreeGangGlassStatusModel deviceStatus;
|
||||
Timer? _timer;
|
||||
|
||||
ThreeGangGlassSwitchBloc({required String deviceId})
|
||||
: deviceStatus = ThreeGangGlassStatusModel(
|
||||
uuid: deviceId,
|
||||
switch1: false,
|
||||
countDown1: 0,
|
||||
switch2: false,
|
||||
countDown2: 0,
|
||||
switch3: false,
|
||||
countDown3: 0),
|
||||
super(ThreeGangGlassSwitchInitial()) {
|
||||
on<ThreeGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||
on<ThreeGangGlassSwitchControl>(_onControl);
|
||||
on<ThreeGangGlassSwitchBatchControl>(_onBatchControl);
|
||||
on<ThreeGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
|
||||
on<ThreeGangGlassFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
Future<void> _onFetchDeviceStatus(
|
||||
ThreeGangGlassSwitchFetchDeviceEvent event, Emitter<ThreeGangGlassSwitchState> emit) async {
|
||||
emit(ThreeGangGlassSwitchLoading());
|
||||
try {
|
||||
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
|
||||
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(ThreeGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onControl(ThreeGangGlassSwitchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceId,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onBatchControl(ThreeGangGlassSwitchBatchControl event, Emitter<ThreeGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFetchBatchStatus(
|
||||
ThreeGangGlassSwitchFetchBatchStatusEvent event, Emitter<ThreeGangGlassSwitchState> emit) async {
|
||||
emit(ThreeGangGlassSwitchLoading());
|
||||
try {
|
||||
final status = await DevicesManagementApi().getBatchStatus(event.deviceIds);
|
||||
deviceStatus = ThreeGangGlassStatusModel.fromJson(event.deviceIds.first, status.status);
|
||||
emit(ThreeGangGlassSwitchBatchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(ThreeGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFactoryReset(ThreeGangGlassFactoryReset event, Emitter<ThreeGangGlassSwitchState> emit) async {
|
||||
emit(ThreeGangGlassSwitchLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(event.factoryReset, event.deviceId);
|
||||
if (!response) {
|
||||
emit(ThreeGangGlassSwitchError('Failed'));
|
||||
} else {
|
||||
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(ThreeGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _runDebounce({
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required bool value,
|
||||
required bool oldValue,
|
||||
required Emitter<ThreeGangGlassSwitchState> 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<ThreeGangGlassSwitchState> emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, bool value) {
|
||||
if (code == 'switch_1') {
|
||||
deviceStatus = deviceStatus.copyWith(switch1: value);
|
||||
} else if (code == 'switch_2') {
|
||||
deviceStatus = deviceStatus.copyWith(switch2: value);
|
||||
} else if (code == 'switch_3') {
|
||||
deviceStatus = deviceStatus.copyWith(switch3: value);
|
||||
}
|
||||
}
|
||||
|
||||
bool _getValueByCode(String code) {
|
||||
switch (code) {
|
||||
case 'switch_1':
|
||||
return deviceStatus.switch1;
|
||||
case 'switch_2':
|
||||
return deviceStatus.switch2;
|
||||
case 'switch_3':
|
||||
return deviceStatus.switch3;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
part of 'three_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ThreeGangGlassSwitchEvent {}
|
||||
|
||||
class ThreeGangGlassSwitchFetchDeviceEvent extends ThreeGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
|
||||
ThreeGangGlassSwitchFetchDeviceEvent(this.deviceId);
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchControl extends ThreeGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
ThreeGangGlassSwitchControl({
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchBatchControl extends ThreeGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
ThreeGangGlassSwitchBatchControl({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchFetchBatchStatusEvent
|
||||
extends ThreeGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
|
||||
ThreeGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
|
||||
}
|
||||
|
||||
class ThreeGangGlassFactoryReset extends ThreeGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
ThreeGangGlassFactoryReset({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
part of 'three_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class ThreeGangGlassSwitchState {}
|
||||
|
||||
class ThreeGangGlassSwitchInitial extends ThreeGangGlassSwitchState {}
|
||||
|
||||
class ThreeGangGlassSwitchLoading extends ThreeGangGlassSwitchState {}
|
||||
|
||||
class ThreeGangGlassSwitchStatusLoaded extends ThreeGangGlassSwitchState {
|
||||
final ThreeGangGlassStatusModel status;
|
||||
|
||||
ThreeGangGlassSwitchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchError extends ThreeGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
ThreeGangGlassSwitchError(this.message);
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchBatchStatusLoaded extends ThreeGangGlassSwitchState {
|
||||
final ThreeGangGlassStatusModel status;
|
||||
|
||||
ThreeGangGlassSwitchBatchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class ThreeGangGlassSwitchBatchControlError extends ThreeGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
ThreeGangGlassSwitchBatchControlError(this.message);
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class ThreeGangGlassStatusModel {
|
||||
final String uuid;
|
||||
final bool switch1;
|
||||
final int countDown1;
|
||||
final bool switch2;
|
||||
final int countDown2;
|
||||
final bool switch3;
|
||||
final int countDown3;
|
||||
|
||||
ThreeGangGlassStatusModel({
|
||||
required this.uuid,
|
||||
required this.switch1,
|
||||
required this.countDown1,
|
||||
required this.switch2,
|
||||
required this.countDown2,
|
||||
required this.switch3,
|
||||
required this.countDown3,
|
||||
});
|
||||
|
||||
factory ThreeGangGlassStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||
late bool switch1;
|
||||
late int countDown1;
|
||||
late bool switch2;
|
||||
late int countDown2;
|
||||
late bool switch3;
|
||||
late int countDown3;
|
||||
|
||||
for (var status in jsonList) {
|
||||
switch (status.code) {
|
||||
case 'switch_1':
|
||||
switch1 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_1':
|
||||
countDown1 = status.value ?? 0;
|
||||
break;
|
||||
case 'switch_2':
|
||||
switch2 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_2':
|
||||
countDown2 = status.value ?? 0;
|
||||
break;
|
||||
case 'switch_3':
|
||||
switch3 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_3':
|
||||
countDown3 = status.value ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ThreeGangGlassStatusModel(
|
||||
uuid: id,
|
||||
switch1: switch1,
|
||||
countDown1: countDown1,
|
||||
switch2: switch2,
|
||||
countDown2: countDown2,
|
||||
switch3: switch3,
|
||||
countDown3: countDown3,
|
||||
);
|
||||
}
|
||||
|
||||
ThreeGangGlassStatusModel copyWith({
|
||||
String? uuid,
|
||||
bool? switch1,
|
||||
int? countDown1,
|
||||
bool? switch2,
|
||||
int? countDown2,
|
||||
bool? switch3,
|
||||
int? countDown3,
|
||||
}) {
|
||||
return ThreeGangGlassStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
switch1: switch1 ?? this.switch1,
|
||||
countDown1: countDown1 ?? this.countDown1,
|
||||
switch2: switch2 ?? this.switch2,
|
||||
countDown2: countDown2 ?? this.countDown2,
|
||||
switch3: switch3 ?? this.switch3,
|
||||
countDown3: countDown3 ?? this.countDown3,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'ThreeGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown1: $countDown1, switch2: $switch2, countDown2: $countDown2, switch3: $switch3, countDown3: $countDown3)';
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
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/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/three_g_glass_switch/models/three_gang_glass_switch.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class ThreeGangGlassSwitchBatchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const ThreeGangGlassSwitchBatchControlView({required this.deviceIds, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => ThreeGangGlassSwitchBloc(deviceId: deviceIds.first)
|
||||
..add(ThreeGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
|
||||
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is ThreeGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is ThreeGangGlassSwitchBatchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is ThreeGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, ThreeGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceIds.first,
|
||||
label: "Wall Light",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch2,
|
||||
code: 'switch_2',
|
||||
deviceId: deviceIds.first,
|
||||
label: "Ceiling Light",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_2',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch3,
|
||||
code: 'switch_3',
|
||||
deviceId: deviceIds.first,
|
||||
label: "SpotLight",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_3',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(
|
||||
deviceId: deviceIds.first,
|
||||
version: 12, // adjust the version according to your requirement
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassFactoryReset(
|
||||
deviceId: status.uuid,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
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/three_g_glass_switch/bloc/three_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
import '../models/three_gang_glass_switch.dart';
|
||||
|
||||
class ThreeGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final String deviceId;
|
||||
|
||||
const ThreeGangGlassSwitchControlView({required this.deviceId, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
ThreeGangGlassSwitchBloc(deviceId: deviceId)..add(ThreeGangGlassSwitchFetchDeviceEvent(deviceId)),
|
||||
child: BlocBuilder<ThreeGangGlassSwitchBloc, ThreeGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is ThreeGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is ThreeGangGlassSwitchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is ThreeGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, ThreeGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceId,
|
||||
label: "Wall Light",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch2,
|
||||
code: 'switch_2',
|
||||
deviceId: deviceId,
|
||||
label: "Ceiling Light",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_2',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch3,
|
||||
code: 'switch_3',
|
||||
deviceId: deviceId,
|
||||
label: "SpotLight",
|
||||
onChange: (value) {
|
||||
context.read<ThreeGangGlassSwitchBloc>().add(
|
||||
ThreeGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_3',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Preferences',
|
||||
icon: Assets.preferences,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Scheduling',
|
||||
icon: Assets.scheduling,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
part 'two_gang_glass_switch_event.dart';
|
||||
part 'two_gang_glass_switch_state.dart';
|
||||
|
||||
class TwoGangGlassSwitchBloc
|
||||
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
|
||||
TwoGangGlassStatusModel deviceStatus;
|
||||
Timer? _timer;
|
||||
|
||||
TwoGangGlassSwitchBloc({required String deviceId})
|
||||
: deviceStatus = TwoGangGlassStatusModel(
|
||||
uuid: deviceId,
|
||||
switch1: false,
|
||||
countDown1: 0,
|
||||
switch2: false,
|
||||
countDown2: 0),
|
||||
super(TwoGangGlassSwitchInitial()) {
|
||||
on<TwoGangGlassSwitchFetchDeviceEvent>(_onFetchDeviceStatus);
|
||||
on<TwoGangGlassSwitchControl>(_onControl);
|
||||
on<TwoGangGlassSwitchBatchControl>(_onBatchControl);
|
||||
on<TwoGangGlassSwitchFetchBatchStatusEvent>(_onFetchBatchStatus);
|
||||
on<TwoGangGlassFactoryReset>(_onFactoryReset);
|
||||
}
|
||||
|
||||
Future<void> _onFetchDeviceStatus(TwoGangGlassSwitchFetchDeviceEvent event,
|
||||
Emitter<TwoGangGlassSwitchState> emit) async {
|
||||
emit(TwoGangGlassSwitchLoading());
|
||||
try {
|
||||
final status =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus =
|
||||
TwoGangGlassStatusModel.fromJson(event.deviceId, status.status);
|
||||
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(TwoGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onControl(TwoGangGlassSwitchControl event,
|
||||
Emitter<TwoGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceId,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onBatchControl(TwoGangGlassSwitchBatchControl event,
|
||||
Emitter<TwoGangGlassSwitchState> emit) async {
|
||||
final oldValue = _getValueByCode(event.code);
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFetchBatchStatus(
|
||||
TwoGangGlassSwitchFetchBatchStatusEvent event,
|
||||
Emitter<TwoGangGlassSwitchState> emit) async {
|
||||
emit(TwoGangGlassSwitchLoading());
|
||||
try {
|
||||
final status =
|
||||
await DevicesManagementApi().getBatchStatus(event.deviceIds);
|
||||
deviceStatus = TwoGangGlassStatusModel.fromJson(
|
||||
event.deviceIds.first, status.status);
|
||||
emit(TwoGangGlassSwitchBatchStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
emit(TwoGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onFactoryReset(TwoGangGlassFactoryReset event,
|
||||
Emitter<TwoGangGlassSwitchState> emit) async {
|
||||
emit(TwoGangGlassSwitchLoading());
|
||||
try {
|
||||
final response = await DevicesManagementApi()
|
||||
.factoryReset(event.factoryReset, event.deviceId);
|
||||
if (!response) {
|
||||
emit(TwoGangGlassSwitchError('Failed'));
|
||||
} else {
|
||||
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(TwoGangGlassSwitchError(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _runDebounce({
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required bool value,
|
||||
required bool oldValue,
|
||||
required Emitter<TwoGangGlassSwitchState> 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<TwoGangGlassSwitchState> emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
emit(TwoGangGlassSwitchStatusLoaded(deviceStatus));
|
||||
}
|
||||
|
||||
void _updateLocalValue(String code, bool value) {
|
||||
if (code == 'switch_1') {
|
||||
deviceStatus = deviceStatus.copyWith(switch1: value);
|
||||
} else 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;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_timer?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
part of 'two_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class TwoGangGlassSwitchEvent {}
|
||||
|
||||
class TwoGangGlassSwitchFetchDeviceEvent extends TwoGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
|
||||
TwoGangGlassSwitchFetchDeviceEvent(this.deviceId);
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchControl extends TwoGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
TwoGangGlassSwitchControl({
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchBatchControl extends TwoGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final bool value;
|
||||
|
||||
TwoGangGlassSwitchBatchControl({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchFetchBatchStatusEvent extends TwoGangGlassSwitchEvent {
|
||||
final List<String> deviceIds;
|
||||
|
||||
TwoGangGlassSwitchFetchBatchStatusEvent(this.deviceIds);
|
||||
}
|
||||
|
||||
class TwoGangGlassFactoryReset extends TwoGangGlassSwitchEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
TwoGangGlassFactoryReset({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
part of 'two_gang_glass_switch_bloc.dart';
|
||||
|
||||
@immutable
|
||||
abstract class TwoGangGlassSwitchState {}
|
||||
|
||||
class TwoGangGlassSwitchInitial extends TwoGangGlassSwitchState {}
|
||||
|
||||
class TwoGangGlassSwitchLoading extends TwoGangGlassSwitchState {}
|
||||
|
||||
class TwoGangGlassSwitchStatusLoaded extends TwoGangGlassSwitchState {
|
||||
final TwoGangGlassStatusModel status;
|
||||
|
||||
TwoGangGlassSwitchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchError extends TwoGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
TwoGangGlassSwitchError(this.message);
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchBatchStatusLoaded extends TwoGangGlassSwitchState {
|
||||
final TwoGangGlassStatusModel status;
|
||||
|
||||
TwoGangGlassSwitchBatchStatusLoaded(this.status);
|
||||
}
|
||||
|
||||
class TwoGangGlassSwitchBatchControlError extends TwoGangGlassSwitchState {
|
||||
final String message;
|
||||
|
||||
TwoGangGlassSwitchBatchControlError(this.message);
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class TwoGangGlassStatusModel {
|
||||
final String uuid;
|
||||
final bool switch1;
|
||||
final int countDown1;
|
||||
final bool switch2;
|
||||
final int countDown2;
|
||||
|
||||
TwoGangGlassStatusModel({
|
||||
required this.uuid,
|
||||
required this.switch1,
|
||||
required this.countDown1,
|
||||
required this.switch2,
|
||||
required this.countDown2,
|
||||
});
|
||||
|
||||
factory TwoGangGlassStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||
late bool switch1;
|
||||
late int countDown1;
|
||||
late bool switch2;
|
||||
late int countDown2;
|
||||
|
||||
for (var status in jsonList) {
|
||||
switch (status.code) {
|
||||
case 'switch_1':
|
||||
switch1 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_1':
|
||||
countDown1 = status.value ?? 0;
|
||||
break;
|
||||
case 'switch_2':
|
||||
switch2 = status.value ?? false;
|
||||
break;
|
||||
case 'countdown_2':
|
||||
countDown2 = status.value ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TwoGangGlassStatusModel(
|
||||
uuid: id,
|
||||
switch1: switch1,
|
||||
countDown1: countDown1,
|
||||
switch2: switch2,
|
||||
countDown2: countDown2,
|
||||
);
|
||||
}
|
||||
|
||||
TwoGangGlassStatusModel copyWith({
|
||||
String? uuid,
|
||||
bool? switch1,
|
||||
int? countDown1,
|
||||
bool? switch2,
|
||||
int? countDown2,
|
||||
}) {
|
||||
return TwoGangGlassStatusModel(
|
||||
uuid: uuid ?? this.uuid,
|
||||
switch1: switch1 ?? this.switch1,
|
||||
countDown1: countDown1 ?? this.countDown1,
|
||||
switch2: switch2 ?? this.switch2,
|
||||
countDown2: countDown2 ?? this.countDown2,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'TwoGangGlassStatusModel(uuid: $uuid, switch1: $switch1, countDown1: $countDown1, switch2: $switch2, countDown2: $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_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class TwoGangGlassSwitchBatchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const TwoGangGlassSwitchBatchControlView({required this.deviceIds, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
TwoGangGlassSwitchBloc(deviceId: deviceIds.first)..add(TwoGangGlassSwitchFetchBatchStatusEvent(deviceIds)),
|
||||
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is TwoGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is TwoGangGlassSwitchBatchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is TwoGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceIds.first,
|
||||
label: 'Wall Light',
|
||||
onChange: (value) {
|
||||
context.read<TwoGangGlassSwitchBloc>().add(
|
||||
TwoGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch2,
|
||||
code: 'switch_2',
|
||||
deviceId: deviceIds.first,
|
||||
label: 'Ceiling Light',
|
||||
onChange: (value) {
|
||||
context.read<TwoGangGlassSwitchBloc>().add(
|
||||
TwoGangGlassSwitchBatchControl(
|
||||
deviceIds: deviceIds,
|
||||
code: 'switch_2',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
FirmwareUpdateWidget(
|
||||
deviceId: deviceIds.first,
|
||||
version: 12, // adjust the version according to your requirement
|
||||
),
|
||||
FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<TwoGangGlassSwitchBloc>().add(
|
||||
TwoGangGlassFactoryReset(
|
||||
deviceId: status.uuid,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
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_g_glass_switch/bloc/two_gang_glass_switch_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class TwoGangGlassSwitchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final String deviceId;
|
||||
|
||||
const TwoGangGlassSwitchControlView({required this.deviceId, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
TwoGangGlassSwitchBloc(deviceId: deviceId)..add(TwoGangGlassSwitchFetchDeviceEvent(deviceId)),
|
||||
child: BlocBuilder<TwoGangGlassSwitchBloc, TwoGangGlassSwitchState>(
|
||||
builder: (context, state) {
|
||||
if (state is TwoGangGlassSwitchLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is TwoGangGlassSwitchStatusLoaded) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is TwoGangGlassSwitchError) {
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(BuildContext context, TwoGangGlassStatusModel 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: [
|
||||
ToggleWidget(
|
||||
value: status.switch1,
|
||||
code: 'switch_1',
|
||||
deviceId: deviceId,
|
||||
label: 'Wall Light',
|
||||
onChange: (value) {
|
||||
context.read<TwoGangGlassSwitchBloc>().add(
|
||||
TwoGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_1',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: status.switch2,
|
||||
code: 'switch_2',
|
||||
deviceId: deviceId,
|
||||
label: 'Ceiling Light',
|
||||
onChange: (value) {
|
||||
context.read<TwoGangGlassSwitchBloc>().add(
|
||||
TwoGangGlassSwitchControl(
|
||||
deviceId: deviceId,
|
||||
code: 'switch_2',
|
||||
value: value,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Preferences',
|
||||
icon: Assets.preferences,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
ToggleWidget(
|
||||
value: false,
|
||||
code: '',
|
||||
deviceId: deviceId,
|
||||
label: 'Scheduling',
|
||||
icon: Assets.scheduling,
|
||||
onChange: (value) {},
|
||||
showToggle: false,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
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/wall_sensor/bloc/event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
@ -38,12 +38,10 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
|
||||
// Fetch batch status
|
||||
FutureOr<void> _fetchWallSensorBatchControl(
|
||||
WallSensorFetchBatchStatusEvent event,
|
||||
Emitter<WallSensorState> emit) async {
|
||||
WallSensorFetchBatchStatusEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(WallSensorLoadingInitialState());
|
||||
try {
|
||||
var response =
|
||||
await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
var response = await DevicesManagementApi().getBatchStatus(event.devicesIds);
|
||||
deviceStatus = WallSensorModel.fromJson(response.status);
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
} catch (e) {
|
||||
@ -70,8 +68,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
// } catch (_) {}
|
||||
// }
|
||||
|
||||
void _changeValue(
|
||||
WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
||||
void _changeValue(WallSensorChangeValueEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(WallSensorLoadingNewSate(wallSensorModel: deviceStatus));
|
||||
if (event.code == 'far_detection') {
|
||||
deviceStatus.farDetection = event.value;
|
||||
@ -128,8 +125,7 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
try {
|
||||
late bool response;
|
||||
if (isBatch) {
|
||||
response = await DevicesManagementApi()
|
||||
.deviceBatchControl(deviceId, code, value);
|
||||
response = await DevicesManagementApi().deviceBatchControl(deviceId, code, value);
|
||||
} else {
|
||||
response = await DevicesManagementApi()
|
||||
.deviceControl(deviceId, Status(code: code, value: value));
|
||||
@ -148,10 +144,13 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
FutureOr<void> _getDeviceReports(
|
||||
GetDeviceReportsEvent event, Emitter<WallSensorState> emit) async {
|
||||
emit(DeviceReportsLoadingState());
|
||||
// final from = DateTime.now().subtract(const Duration(days: 30)).millisecondsSinceEpoch;
|
||||
// final to = DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
try {
|
||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code)
|
||||
.then((value) {
|
||||
// await DevicesManagementApi.getDeviceReportsByDate(
|
||||
// deviceId, event.code, from.toString(), to.toString())
|
||||
await DevicesManagementApi.getDeviceReports(deviceId, event.code).then((value) {
|
||||
emit(DeviceReportsState(deviceReport: value, code: event.code));
|
||||
});
|
||||
} catch (e) {
|
||||
@ -160,13 +159,11 @@ class WallSensorBloc extends Bloc<WallSensorEvent, WallSensorState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _showDescription(
|
||||
ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
|
||||
void _showDescription(ShowDescriptionEvent event, Emitter<WallSensorState> emit) {
|
||||
emit(WallSensorShowDescriptionState(description: event.description));
|
||||
}
|
||||
|
||||
void _backToGridView(
|
||||
BackToGridViewEvent event, Emitter<WallSensorState> emit) {
|
||||
void _backToGridView(BackToGridViewEvent event, Emitter<WallSensorState> emit) {
|
||||
emit(WallSensorUpdateState(wallSensorModel: deviceStatus));
|
||||
}
|
||||
|
@ -4,14 +4,13 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_re
|
||||
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_update_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor_model.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WallSensorBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
class WallSensorBatchControlView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const WallSensorBatchControlView({super.key, required this.devicesIds});
|
||||
|
||||
final List<String> devicesIds;
|
||||
@ -26,16 +25,13 @@ class WallSensorBatchControlView extends StatelessWidget
|
||||
..add(WallSensorFetchBatchStatusEvent(devicesIds)),
|
||||
child: BlocBuilder<WallSensorBloc, WallSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is WallSensorLoadingInitialState ||
|
||||
state is DeviceReportsLoadingState) {
|
||||
if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WallSensorUpdateState) {
|
||||
return _buildGridView(context, state.wallSensorModel, isExtraLarge,
|
||||
isLarge, isMedium);
|
||||
return _buildGridView(context, state.wallSensorModel, isExtraLarge, isLarge, isMedium);
|
||||
} else if (state is DeviceReportsFailedState) {
|
||||
final model = context.read<WallSensorBloc>().deviceStatus;
|
||||
return _buildGridView(
|
||||
context, model, isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
@ -43,8 +39,8 @@ class WallSensorBatchControlView extends StatelessWidget
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, WallSensorModel model,
|
||||
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
Widget _buildGridView(
|
||||
BuildContext context, WallSensorModel model, bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50, vertical: 20),
|
||||
shrinkWrap: true,
|
||||
@ -97,12 +93,11 @@ class WallSensorBatchControlView extends StatelessWidget
|
||||
maxValue: 10000,
|
||||
steps: 1,
|
||||
description: 'sec',
|
||||
action: (int value) =>
|
||||
context.read<WallSensorBloc>().add(WallSensorBatchControlEvent(
|
||||
deviceIds: devicesIds,
|
||||
code: 'no_one_time',
|
||||
value: value,
|
||||
))),
|
||||
action: (int value) => context.read<WallSensorBloc>().add(WallSensorBatchControlEvent(
|
||||
deviceIds: devicesIds,
|
||||
code: 'no_one_time',
|
||||
value: value,
|
||||
))),
|
||||
PresenceUpdateData(
|
||||
value: model.farDetection.toDouble(),
|
||||
title: 'Far Detection:',
|
||||
|
@ -3,9 +3,9 @@ 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/shared/table/description_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/wall_sensor/bloc/wall_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_display_data.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_static_widget.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/sensors_widgets/presence_status.dart';
|
||||
@ -14,8 +14,7 @@ import 'package:syncrow_web/pages/device_managment/wall_sensor/model/wall_sensor
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WallSensorControlsView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
class WallSensorControlsView extends StatelessWidget with HelperResponsiveLayout {
|
||||
const WallSensorControlsView({super.key, required this.device});
|
||||
|
||||
final AllDevicesModel device;
|
||||
@ -26,23 +25,19 @@ class WallSensorControlsView extends StatelessWidget
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return BlocProvider(
|
||||
create: (context) => WallSensorBloc(deviceId: device.uuid!)
|
||||
..add(WallSensorFetchStatusEvent()),
|
||||
create: (context) =>
|
||||
WallSensorBloc(deviceId: device.uuid!)..add(WallSensorFetchStatusEvent()),
|
||||
child: BlocBuilder<WallSensorBloc, WallSensorState>(
|
||||
builder: (context, state) {
|
||||
if (state is WallSensorLoadingInitialState ||
|
||||
state is DeviceReportsLoadingState) {
|
||||
if (state is WallSensorLoadingInitialState || state is DeviceReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WallSensorUpdateState) {
|
||||
return _buildGridView(context, state.wallSensorModel, isExtraLarge,
|
||||
isLarge, isMedium);
|
||||
return _buildGridView(context, state.wallSensorModel, isExtraLarge, isLarge, isMedium);
|
||||
} else if (state is DeviceReportsState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
thirdColumnTitle:
|
||||
state.code == 'illuminance_value' ? "Value" : 'Status',
|
||||
thirdColumnDescription:
|
||||
state.code == 'illuminance_value' ? "Lux" : null,
|
||||
thirdColumnTitle: state.code == 'illuminance_value' ? "Value" : 'Status',
|
||||
thirdColumnDescription: state.code == 'illuminance_value' ? "Lux" : null,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context.read<WallSensorBloc>().add(BackToGridViewEvent());
|
||||
@ -57,8 +52,7 @@ class WallSensorControlsView extends StatelessWidget
|
||||
);
|
||||
} else if (state is DeviceReportsFailedState) {
|
||||
final model = context.read<WallSensorBloc>().deviceStatus;
|
||||
return _buildGridView(
|
||||
context, model, isExtraLarge, isLarge, isMedium);
|
||||
return _buildGridView(context, model, isExtraLarge, isLarge, isMedium);
|
||||
}
|
||||
return const Center(child: Text('Error fetching status'));
|
||||
},
|
||||
@ -66,8 +60,8 @@ class WallSensorControlsView extends StatelessWidget
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGridView(BuildContext context, WallSensorModel model,
|
||||
bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
Widget _buildGridView(
|
||||
BuildContext context, WallSensorModel model, bool isExtraLarge, bool isLarge, bool isMedium) {
|
||||
return GridView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
shrinkWrap: true,
|
||||
@ -136,11 +130,10 @@ class WallSensorControlsView extends StatelessWidget
|
||||
maxValue: 10000,
|
||||
steps: 1,
|
||||
description: 'sec',
|
||||
action: (int value) =>
|
||||
context.read<WallSensorBloc>().add(WallSensorChangeValueEvent(
|
||||
code: 'no_one_time',
|
||||
value: value,
|
||||
))),
|
||||
action: (int value) => context.read<WallSensorBloc>().add(WallSensorChangeValueEvent(
|
||||
code: 'no_one_time',
|
||||
value: value,
|
||||
))),
|
||||
PresenceUpdateData(
|
||||
value: model.farDetection.toDouble(),
|
||||
title: 'Far Detection:',
|
||||
@ -157,8 +150,9 @@ class WallSensorControlsView extends StatelessWidget
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<WallSensorBloc>().add(GetDeviceReportsEvent(
|
||||
code: 'illuminance_value', deviceUuid: device.uuid!));
|
||||
context
|
||||
.read<WallSensorBloc>()
|
||||
.add(GetDeviceReportsEvent(code: 'illuminance_value', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.illuminanceRecordIcon,
|
||||
@ -167,8 +161,9 @@ class WallSensorControlsView extends StatelessWidget
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
context.read<WallSensorBloc>().add(GetDeviceReportsEvent(
|
||||
code: 'presence_state', deviceUuid: device.uuid!));
|
||||
context
|
||||
.read<WallSensorBloc>()
|
||||
.add(GetDeviceReportsEvent(code: 'presence_state', deviceUuid: device.uuid!));
|
||||
},
|
||||
child: const PresenceStaticWidget(
|
||||
icon: Assets.presenceRecordIcon,
|
||||
|
@ -1,6 +1,7 @@
|
||||
// water_heater_bloc.dart
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -30,6 +31,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
|
||||
on<GetSchedulesEvent>(_getSchedule);
|
||||
on<AddScheduleEvent>(_onAddSchedule);
|
||||
on<EditWaterHeaterScheduleEvent>(_onEditSchedule);
|
||||
on<DeleteScheduleEvent>(_onDeleteSchedule);
|
||||
on<UpdateScheduleEntryEvent>(_onUpdateSchedule);
|
||||
}
|
||||
@ -76,8 +78,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
final updatedDays = List<bool>.from(currentState.selectedDays);
|
||||
updatedDays[event.index] = event.value;
|
||||
emit(currentState.copyWith(
|
||||
selectedDays: updatedDays, selectedTime: currentState.selectedTime));
|
||||
emit(currentState.copyWith(selectedDays: updatedDays, selectedTime: currentState.selectedTime));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateFunctionOn(
|
||||
@ -85,8 +86,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
Emitter<WaterHeaterState> emit,
|
||||
) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
emit(currentState.copyWith(
|
||||
functionOn: event.isOn, selectedTime: currentState.selectedTime));
|
||||
emit(currentState.copyWith(functionOn: event.isOn, selectedTime: currentState.selectedTime));
|
||||
}
|
||||
|
||||
FutureOr<void> _updateScheduleEvent(
|
||||
@ -101,8 +101,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
));
|
||||
}
|
||||
if (event.scheduleMode == ScheduleModes.countdown) {
|
||||
final countdownRemaining =
|
||||
Duration(hours: event.hours, minutes: event.minutes);
|
||||
final countdownRemaining = Duration(hours: event.hours, minutes: event.minutes);
|
||||
|
||||
emit(currentState.copyWith(
|
||||
scheduleMode: ScheduleModes.countdown,
|
||||
@ -112,13 +111,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
countdownRemaining: countdownRemaining,
|
||||
));
|
||||
|
||||
if (!currentState.isCountdownActive! &&
|
||||
countdownRemaining > Duration.zero) {
|
||||
if (!currentState.isCountdownActive! && countdownRemaining > Duration.zero) {
|
||||
_startCountdownTimer(emit, countdownRemaining);
|
||||
}
|
||||
} else if (event.scheduleMode == ScheduleModes.inching) {
|
||||
final inchingDuration =
|
||||
Duration(hours: event.hours, minutes: event.minutes);
|
||||
final inchingDuration = Duration(hours: event.hours, minutes: event.minutes);
|
||||
|
||||
emit(currentState.copyWith(
|
||||
scheduleMode: ScheduleModes.inching,
|
||||
@ -220,8 +217,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
try {
|
||||
final status = await DevicesManagementApi().deviceControl(
|
||||
event.deviceId,
|
||||
Status(
|
||||
code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
|
||||
Status(code: isCountDown ? 'countdown_1' : 'switch_inching', value: 0),
|
||||
);
|
||||
if (!status) {
|
||||
emit(const WaterHeaterFailedState(error: 'Failed to stop schedule.'));
|
||||
@ -239,10 +235,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
emit(WaterHeaterLoadingState());
|
||||
|
||||
try {
|
||||
final status =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus =
|
||||
WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
|
||||
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
|
||||
|
||||
if (deviceStatus.scheduleMode == ScheduleModes.countdown) {
|
||||
final countdownRemaining = Duration(
|
||||
@ -340,10 +334,8 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
if (currentState.countdownRemaining != null &&
|
||||
currentState.countdownRemaining! > Duration.zero) {
|
||||
final newRemaining =
|
||||
currentState.countdownRemaining! - const Duration(minutes: 1);
|
||||
if (currentState.countdownRemaining != null && currentState.countdownRemaining! > Duration.zero) {
|
||||
final newRemaining = currentState.countdownRemaining! - const Duration(minutes: 1);
|
||||
|
||||
if (newRemaining <= Duration.zero) {
|
||||
_countdownTimer?.cancel();
|
||||
@ -438,8 +430,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
}
|
||||
}
|
||||
|
||||
void _revertValue(String code, dynamic oldValue,
|
||||
void Function(WaterHeaterState state) emit) {
|
||||
void _revertValue(String code, dynamic oldValue, void Function(WaterHeaterState state) emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
@ -486,13 +477,12 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
return super.close();
|
||||
}
|
||||
|
||||
FutureOr<void> _getSchedule(
|
||||
GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
FutureOr<void> _getSchedule(GetSchedulesEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
emit(ScheduleLoadingState());
|
||||
|
||||
try {
|
||||
List<ScheduleModel> schedules = await DevicesManagementApi()
|
||||
.getDeviceSchedules(deviceStatus.uuid, event.category);
|
||||
List<ScheduleModel> schedules =
|
||||
await DevicesManagementApi().getDeviceSchedules(deviceStatus.uuid, event.category);
|
||||
|
||||
emit(WaterHeaterDeviceStatusLoaded(
|
||||
deviceStatus,
|
||||
@ -524,8 +514,35 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi()
|
||||
.addScheduleRecord(newSchedule, currentState.status.uuid);
|
||||
bool success = await DevicesManagementApi().addScheduleRecord(newSchedule, currentState.status.uuid);
|
||||
|
||||
if (success) {
|
||||
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid));
|
||||
} else {
|
||||
emit(currentState);
|
||||
//emit(const WaterHeaterFailedState(error: 'Failed to add schedule.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _onEditSchedule(EditWaterHeaterScheduleEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
ScheduleEntry newSchedule = ScheduleEntry(
|
||||
scheduleId: event.scheduleId,
|
||||
category: event.category,
|
||||
time: formatTimeOfDayToISO(event.time),
|
||||
function: Status(code: 'switch_1', value: event.functionOn),
|
||||
days: ScheduleModel.convertSelectedDaysToStrings(event.selectedDays),
|
||||
);
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi().editScheduleRecord(
|
||||
currentState.status.uuid,
|
||||
newSchedule,
|
||||
);
|
||||
|
||||
if (success) {
|
||||
add(GetSchedulesEvent(category: 'switch_1', uuid: deviceStatus.uuid));
|
||||
@ -577,13 +594,11 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
|
||||
// emit(ScheduleLoadingState());
|
||||
|
||||
bool success = await DevicesManagementApi()
|
||||
.deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
|
||||
bool success = await DevicesManagementApi().deleteScheduleRecord(currentState.status.uuid, event.scheduleId);
|
||||
|
||||
if (success) {
|
||||
final updatedSchedules = currentState.schedules
|
||||
.where((schedule) => schedule.scheduleId != event.scheduleId)
|
||||
.toList();
|
||||
final updatedSchedules =
|
||||
currentState.schedules.where((schedule) => schedule.scheduleId != event.scheduleId).toList();
|
||||
|
||||
emit(currentState.copyWith(schedules: updatedSchedules));
|
||||
} else {
|
||||
@ -593,15 +608,12 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event,
|
||||
Emitter<WaterHeaterState> emit) async {
|
||||
FutureOr<void> _batchFetchWaterHeater(FetchWaterHeaterBatchStatusEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
emit(WaterHeaterLoadingState());
|
||||
|
||||
try {
|
||||
final status =
|
||||
await DevicesManagementApi().getBatchStatus(event.devicesUuid);
|
||||
deviceStatus = WaterHeaterStatusModel.fromJson(
|
||||
event.devicesUuid.first, status.status);
|
||||
final status = await DevicesManagementApi().getBatchStatus(event.devicesUuid);
|
||||
deviceStatus = WaterHeaterStatusModel.fromJson(event.devicesUuid.first, status.status);
|
||||
|
||||
emit(WaterHeaterDeviceStatusLoaded(deviceStatus));
|
||||
} catch (e) {
|
||||
@ -609,8 +621,7 @@ class WaterHeaterBloc extends Bloc<WaterHeaterEvent, WaterHeaterState> {
|
||||
}
|
||||
}
|
||||
|
||||
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event,
|
||||
Emitter<WaterHeaterState> emit) async {
|
||||
FutureOr<void> _batchControlWaterHeater(ControlWaterHeaterBatchEvent event, Emitter<WaterHeaterState> emit) async {
|
||||
if (state is WaterHeaterDeviceStatusLoaded) {
|
||||
final currentState = state as WaterHeaterDeviceStatusLoaded;
|
||||
|
||||
|
@ -71,6 +71,22 @@ final class AddScheduleEvent extends WaterHeaterEvent {
|
||||
List<Object?> get props => [selectedDays, time, functionOn, category];
|
||||
}
|
||||
|
||||
class EditWaterHeaterScheduleEvent extends WaterHeaterEvent {
|
||||
final String scheduleId;
|
||||
final String category;
|
||||
final TimeOfDay time;
|
||||
final bool functionOn;
|
||||
final List<bool> selectedDays;
|
||||
|
||||
const EditWaterHeaterScheduleEvent({
|
||||
required this.scheduleId,
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.functionOn,
|
||||
required this.selectedDays,
|
||||
});
|
||||
}
|
||||
|
||||
final class DeleteScheduleEvent extends WaterHeaterEvent {
|
||||
final int index;
|
||||
final String scheduleId;
|
||||
|
@ -1,14 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/bloc/water_heater_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
|
||||
class ScheduleDialogHelper {
|
||||
static void showAddScheduleDialog(BuildContext context,
|
||||
{ScheduleModel? schedule, int? index, bool? isEdit}) {
|
||||
static void showAddScheduleDialog(BuildContext context, {ScheduleModel? schedule, int? index, bool? isEdit}) {
|
||||
final bloc = context.read<WaterHeaterBloc>();
|
||||
|
||||
if (schedule == null) {
|
||||
@ -70,35 +69,30 @@ class ScheduleDialogHelper {
|
||||
padding: 8,
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
borderRadius: 15,
|
||||
onPressed: isEdit == true
|
||||
? null
|
||||
: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime:
|
||||
state.selectedTime ?? TimeOfDay.now(),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (time != null) {
|
||||
bloc.add(UpdateSelectedTimeEvent(time));
|
||||
}
|
||||
},
|
||||
onPressed: () async {
|
||||
TimeOfDay? time = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: state.selectedTime ?? TimeOfDay.now(),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: ColorsManager.primaryColor,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
if (time != null) {
|
||||
bloc.add(UpdateSelectedTimeEvent(time));
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
state.selectedTime == null
|
||||
? 'Time'
|
||||
: state.selectedTime!.format(context),
|
||||
state.selectedTime == null ? 'Time' : state.selectedTime!.format(context),
|
||||
style: context.textTheme.bodySmall!.copyWith(
|
||||
color: ColorsManager.grayColor,
|
||||
),
|
||||
@ -113,8 +107,7 @@ class ScheduleDialogHelper {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_buildDayCheckboxes(context, state.selectedDays,
|
||||
isEdit: isEdit),
|
||||
_buildDayCheckboxes(context, state.selectedDays, isEdit: isEdit),
|
||||
const SizedBox(height: 16),
|
||||
_buildFunctionSwitch(context, state.functionOn, isEdit),
|
||||
],
|
||||
@ -141,7 +134,13 @@ class ScheduleDialogHelper {
|
||||
onPressed: () {
|
||||
if (state.selectedTime != null) {
|
||||
if (state.isEditing && index != null) {
|
||||
return;
|
||||
bloc.add(EditWaterHeaterScheduleEvent(
|
||||
scheduleId: schedule?.scheduleId ?? '',
|
||||
category: 'switch_1',
|
||||
time: state.selectedTime!,
|
||||
selectedDays: state.selectedDays,
|
||||
functionOn: state.functionOn,
|
||||
));
|
||||
} else {
|
||||
bloc.add(AddScheduleEvent(
|
||||
category: 'switch_1',
|
||||
@ -193,9 +192,7 @@ class ScheduleDialogHelper {
|
||||
return daysBoolean;
|
||||
}
|
||||
|
||||
static Widget _buildDayCheckboxes(
|
||||
BuildContext context, List<bool> selectedDays,
|
||||
{bool? isEdit}) {
|
||||
static Widget _buildDayCheckboxes(BuildContext context, List<bool> selectedDays, {bool? isEdit}) {
|
||||
final dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
||||
|
||||
return Row(
|
||||
@ -204,13 +201,9 @@ class ScheduleDialogHelper {
|
||||
children: [
|
||||
Checkbox(
|
||||
value: selectedDays[index],
|
||||
onChanged: isEdit == true
|
||||
? null
|
||||
: (bool? value) {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(UpdateSelectedDayEvent(index, value!));
|
||||
},
|
||||
onChanged: (bool? value) {
|
||||
context.read<WaterHeaterBloc>().add(UpdateSelectedDayEvent(index, value!));
|
||||
},
|
||||
),
|
||||
Text(dayLabels[index]),
|
||||
],
|
||||
@ -219,27 +212,19 @@ class ScheduleDialogHelper {
|
||||
);
|
||||
}
|
||||
|
||||
static Widget _buildFunctionSwitch(
|
||||
BuildContext context, bool isOn, bool? isEdit) {
|
||||
static Widget _buildFunctionSwitch(BuildContext context, bool isOn, bool? isEdit) {
|
||||
return Row(
|
||||
children: [
|
||||
Text(
|
||||
'Function:',
|
||||
style: context.textTheme.bodySmall!
|
||||
.copyWith(color: ColorsManager.grayColor),
|
||||
style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.grayColor),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Radio<bool>(
|
||||
value: true,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
if (isEdit == true) {
|
||||
return;
|
||||
} else {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(true));
|
||||
}
|
||||
context.read<WaterHeaterBloc>().add(const UpdateFunctionOnEvent(true));
|
||||
},
|
||||
),
|
||||
const Text('On'),
|
||||
@ -248,13 +233,7 @@ class ScheduleDialogHelper {
|
||||
value: false,
|
||||
groupValue: isOn,
|
||||
onChanged: (bool? value) {
|
||||
if (isEdit == true) {
|
||||
return;
|
||||
} else {
|
||||
context
|
||||
.read<WaterHeaterBloc>()
|
||||
.add(const UpdateFunctionOnEvent(false));
|
||||
}
|
||||
context.read<WaterHeaterBloc>().add(const UpdateFunctionOnEvent(false));
|
||||
},
|
||||
),
|
||||
const Text('Off'),
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class ScheduleEntry {
|
||||
@ -9,12 +8,14 @@ class ScheduleEntry {
|
||||
final String time;
|
||||
final Status function;
|
||||
final List<String> days;
|
||||
final String? scheduleId;
|
||||
|
||||
ScheduleEntry({
|
||||
required this.category,
|
||||
required this.time,
|
||||
required this.function,
|
||||
required this.days,
|
||||
this.scheduleId,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -38,6 +39,7 @@ class ScheduleEntry {
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
'scheduleId': scheduleId,
|
||||
'category': category,
|
||||
'time': time,
|
||||
'function': function.toMap(),
|
||||
@ -56,8 +58,7 @@ class ScheduleEntry {
|
||||
|
||||
String toJson() => json.encode(toMap());
|
||||
|
||||
factory ScheduleEntry.fromJson(String source) =>
|
||||
ScheduleEntry.fromMap(json.decode(source));
|
||||
factory ScheduleEntry.fromJson(String source) => ScheduleEntry.fromMap(json.decode(source));
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
@ -72,9 +73,6 @@ class ScheduleEntry {
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return category.hashCode ^
|
||||
time.hashCode ^
|
||||
function.hashCode ^
|
||||
days.hashCode;
|
||||
return category.hashCode ^ time.hashCode ^ function.hashCode ^ days.hashCode;
|
||||
}
|
||||
}
|
||||
|
@ -1,76 +0,0 @@
|
||||
// import 'package:flutter/material.dart';
|
||||
// import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_model.dart';
|
||||
// import 'package:syncrow_web/utils/format_date_time.dart';
|
||||
// import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
// class ScheduleRowWidget extends StatelessWidget {
|
||||
// final ScheduleModel schedule;
|
||||
// final int index;
|
||||
// final Function onEdit;
|
||||
// final Function onDelete;
|
||||
|
||||
// const ScheduleRowWidget({
|
||||
// super.key,
|
||||
// required this.schedule,
|
||||
// required this.index,
|
||||
// required this.onEdit,
|
||||
// required this.onDelete,
|
||||
// });
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Table(
|
||||
// border: TableBorder.all(color: ColorsManager.graysColor),
|
||||
// defaultVerticalAlignment: TableCellVerticalAlignment.middle,
|
||||
// children: [
|
||||
// TableRow(
|
||||
// children: [
|
||||
// Center(
|
||||
// child: schedule.enable
|
||||
// ? const Icon(Icons.radio_button_checked,
|
||||
// color: ColorsManager.blueColor)
|
||||
// : const Icon(Icons.radio_button_unchecked),
|
||||
// ),
|
||||
// Center(child: Text(_getSelectedDays(schedule.selectedDays ?? []))),
|
||||
// Center(child: Text(formatIsoStringToTime(schedule.time, context))),
|
||||
// Center(child: Text(schedule.enable ? 'On' : 'Off')),
|
||||
// Center(
|
||||
// child: Wrap(
|
||||
// runAlignment: WrapAlignment.center,
|
||||
// children: [
|
||||
// TextButton(
|
||||
// style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
// onPressed: () => onEdit(),
|
||||
// child: const Text(
|
||||
// 'Edit',
|
||||
// style: TextStyle(color: ColorsManager.blueColor),
|
||||
// ),
|
||||
// ),
|
||||
// TextButton(
|
||||
// style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||
// onPressed: () => onDelete(),
|
||||
// child: const Text(
|
||||
// 'Delete',
|
||||
// style: TextStyle(color: ColorsManager.blueColor),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
// String _getSelectedDays(List<bool> selectedDays) {
|
||||
// final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
// List<String> selectedDaysStr = [];
|
||||
// for (int i = 0; i < selectedDays.length; i++) {
|
||||
// if (selectedDays[i]) {
|
||||
// selectedDaysStr.add(days[i]);
|
||||
// }
|
||||
// }
|
||||
// return selectedDaysStr.join(', ');
|
||||
// }
|
||||
// }
|
173
lib/pages/device_managment/water_leak/bloc/water_leak_bloc.dart
Normal file
@ -0,0 +1,173 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_reports.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:syncrow_web/services/devices_mang_api.dart';
|
||||
|
||||
class WaterLeakBloc extends Bloc<WaterLeakEvent, WaterLeakState> {
|
||||
WaterLeakStatusModel? deviceStatus;
|
||||
Timer? _timer;
|
||||
final String deviceId;
|
||||
|
||||
WaterLeakBloc(this.deviceId) : super(WaterLeakInitialState()) {
|
||||
on<FetchWaterLeakStatusEvent>(_onFetchWaterLeakStatus);
|
||||
on<WaterLeakControlEvent>(_onControl);
|
||||
on<WaterLeakBatchControlEvent>(_onBatchControl);
|
||||
on<FetchWaterLeakBatchStatusEvent>(_onFetchBatchStatus);
|
||||
on<FetchWaterLeakReportsEvent>(_onFetchWaterLeakReports);
|
||||
on<WaterLeakFactoryResetEvent>(_onFactoryReset);
|
||||
}
|
||||
|
||||
Future<void> _onFetchWaterLeakStatus(
|
||||
FetchWaterLeakStatusEvent event, Emitter<WaterLeakState> emit) async {
|
||||
emit(WaterLeakLoadingState());
|
||||
try {
|
||||
final response =
|
||||
await DevicesManagementApi().getDeviceStatus(event.deviceId);
|
||||
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
|
||||
emit(WaterLeakLoadedState(deviceStatus!));
|
||||
} catch (e) {
|
||||
emit(WaterLeakErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onControl(
|
||||
WaterLeakControlEvent event, Emitter<WaterLeakState> emit) async {
|
||||
final oldValue = deviceStatus!.watersensorState;
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(WaterLeakLoadedState(deviceStatus!));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceId,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFactoryReset(
|
||||
WaterLeakFactoryResetEvent event, Emitter<WaterLeakState> emit) async {
|
||||
emit(WaterLeakLoadingState());
|
||||
try {
|
||||
final response = await DevicesManagementApi().factoryReset(
|
||||
event.factoryReset,
|
||||
event.deviceId,
|
||||
);
|
||||
if (response) {
|
||||
emit(WaterLeakInitialState());
|
||||
} else {
|
||||
emit(const WaterLeakErrorState('Factory reset failed'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(WaterLeakErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onBatchControl(
|
||||
WaterLeakBatchControlEvent event, Emitter<WaterLeakState> emit) async {
|
||||
final oldValue = deviceStatus!.watersensorState;
|
||||
|
||||
_updateLocalValue(event.code, event.value);
|
||||
emit(WaterLeakBatchStatusLoadedState(deviceStatus!));
|
||||
|
||||
await _runDebounce(
|
||||
deviceId: event.deviceIds,
|
||||
code: event.code,
|
||||
value: event.value,
|
||||
oldValue: oldValue,
|
||||
emit: emit,
|
||||
isBatch: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onFetchBatchStatus(FetchWaterLeakBatchStatusEvent event,
|
||||
Emitter<WaterLeakState> emit) async {
|
||||
emit(WaterLeakLoadingState());
|
||||
try {
|
||||
final response =
|
||||
await DevicesManagementApi().getBatchStatus(event.deviceIds);
|
||||
deviceStatus = WaterLeakStatusModel.fromJson(deviceId, response.status);
|
||||
emit(WaterLeakBatchStatusLoadedState(deviceStatus!));
|
||||
} catch (e) {
|
||||
emit(WaterLeakErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _runDebounce({
|
||||
required dynamic deviceId,
|
||||
required String code,
|
||||
required dynamic value,
|
||||
required dynamic oldValue,
|
||||
required Emitter<WaterLeakState> 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 _updateLocalValue(String code, dynamic value) {
|
||||
if (code == 'watersensor_state') {
|
||||
deviceStatus = deviceStatus!.copyWith(watersensorState: value);
|
||||
} else if (code == 'battery_percentage') {
|
||||
deviceStatus = deviceStatus!.copyWith(batteryPercentage: value);
|
||||
}
|
||||
}
|
||||
|
||||
void _revertValueAndEmit(String deviceId, String code, dynamic oldValue,
|
||||
Emitter<WaterLeakState> emit) {
|
||||
_updateLocalValue(code, oldValue);
|
||||
emit(WaterLeakLoadedState(deviceStatus!));
|
||||
}
|
||||
|
||||
Future<void> _onFetchWaterLeakReports(
|
||||
FetchWaterLeakReportsEvent event, Emitter<WaterLeakState> emit) async {
|
||||
emit(WaterLeakReportsLoadingState());
|
||||
try {
|
||||
final from = DateTime.now()
|
||||
.subtract(const Duration(days: 30))
|
||||
.millisecondsSinceEpoch;
|
||||
final to = DateTime.now().millisecondsSinceEpoch;
|
||||
final DeviceReport records =
|
||||
await DevicesManagementApi.getDeviceReportsByDate(
|
||||
event.deviceId, event.code, from.toString(), to.toString());
|
||||
emit(WaterLeakReportsLoadedState(records));
|
||||
} catch (e) {
|
||||
emit(WaterLeakReportsFailedState(e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
|
||||
|
||||
abstract class WaterLeakEvent extends Equatable {
|
||||
const WaterLeakEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class FetchWaterLeakStatusEvent extends WaterLeakEvent {
|
||||
final String deviceId;
|
||||
|
||||
const FetchWaterLeakStatusEvent(this.deviceId);
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId];
|
||||
}
|
||||
|
||||
class WaterLeakControlEvent extends WaterLeakEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final dynamic value;
|
||||
|
||||
const WaterLeakControlEvent({
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, code, value];
|
||||
}
|
||||
|
||||
class WaterLeakBatchControlEvent extends WaterLeakEvent {
|
||||
final List<String> deviceIds;
|
||||
final String code;
|
||||
final dynamic value;
|
||||
|
||||
const WaterLeakBatchControlEvent({
|
||||
required this.deviceIds,
|
||||
required this.code,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceIds, code, value];
|
||||
}
|
||||
|
||||
class FetchWaterLeakBatchStatusEvent extends WaterLeakEvent {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const FetchWaterLeakBatchStatusEvent(this.deviceIds);
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceIds];
|
||||
}
|
||||
|
||||
class FetchWaterLeakReportsEvent extends WaterLeakEvent {
|
||||
final String deviceId;
|
||||
final String code;
|
||||
final int from;
|
||||
final int to;
|
||||
|
||||
const FetchWaterLeakReportsEvent({
|
||||
required this.deviceId,
|
||||
required this.code,
|
||||
required this.from,
|
||||
required this.to,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId, code, from, to];
|
||||
}
|
||||
|
||||
class WaterLeakFactoryResetEvent extends WaterLeakEvent {
|
||||
final String deviceId;
|
||||
final FactoryResetModel factoryReset;
|
||||
|
||||
const WaterLeakFactoryResetEvent({
|
||||
required this.deviceId,
|
||||
required this.factoryReset,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceId];
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
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/water_leak/model/water_leak_status_model.dart';
|
||||
|
||||
abstract class WaterLeakState extends Equatable {
|
||||
const WaterLeakState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class WaterLeakInitialState extends WaterLeakState {}
|
||||
|
||||
class WaterLeakLoadingState extends WaterLeakState {}
|
||||
|
||||
class WaterLeakLoadedState extends WaterLeakState {
|
||||
final WaterLeakStatusModel status;
|
||||
|
||||
const WaterLeakLoadedState(this.status);
|
||||
|
||||
@override
|
||||
List<Object> get props => [status];
|
||||
}
|
||||
|
||||
class WaterLeakBatchStatusLoadedState extends WaterLeakState {
|
||||
final WaterLeakStatusModel status;
|
||||
|
||||
const WaterLeakBatchStatusLoadedState(this.status);
|
||||
|
||||
@override
|
||||
List<Object> get props => [status];
|
||||
}
|
||||
|
||||
class WaterLeakErrorState extends WaterLeakState {
|
||||
final String message;
|
||||
|
||||
const WaterLeakErrorState(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
class WaterLeakReportsLoadingState extends WaterLeakState {}
|
||||
|
||||
class WaterLeakReportsLoadedState extends WaterLeakState {
|
||||
final DeviceReport deviceReport;
|
||||
|
||||
const WaterLeakReportsLoadedState(this.deviceReport);
|
||||
|
||||
@override
|
||||
List<Object> get props => [deviceReport];
|
||||
}
|
||||
|
||||
class WaterLeakReportsFailedState extends WaterLeakState {
|
||||
final String error;
|
||||
|
||||
const WaterLeakReportsFailedState(this.error);
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
|
||||
|
||||
class WaterLeakStatusModel {
|
||||
final String productUuid;
|
||||
final String productType;
|
||||
final String watersensorState;
|
||||
final int batteryPercentage;
|
||||
|
||||
WaterLeakStatusModel({
|
||||
required this.productUuid,
|
||||
required this.productType,
|
||||
required this.watersensorState,
|
||||
required this.batteryPercentage,
|
||||
});
|
||||
factory WaterLeakStatusModel.fromJson(String id, List<Status> jsonList) {
|
||||
late String watersensorState;
|
||||
late int batteryPercentage;
|
||||
|
||||
for (var i = 0; i < jsonList.length; i++) {
|
||||
if (jsonList[i].code == 'watersensor_state') {
|
||||
watersensorState = jsonList[i].value;
|
||||
} else if (jsonList[i].code == 'battery_percentage') {
|
||||
batteryPercentage = jsonList[i].value;
|
||||
}
|
||||
}
|
||||
|
||||
return WaterLeakStatusModel(
|
||||
productUuid: id,
|
||||
productType: 'WL',
|
||||
watersensorState: watersensorState,
|
||||
batteryPercentage: batteryPercentage,
|
||||
);
|
||||
}
|
||||
|
||||
WaterLeakStatusModel copyWith({
|
||||
String? productUuid,
|
||||
String? productType,
|
||||
String? watersensorState,
|
||||
int? batteryPercentage,
|
||||
}) {
|
||||
return WaterLeakStatusModel(
|
||||
productUuid: productUuid ?? this.productUuid,
|
||||
productType: productType ?? this.productType,
|
||||
watersensorState: watersensorState ?? this.watersensorState,
|
||||
batteryPercentage: batteryPercentage ?? this.batteryPercentage,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
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/water_leak/bloc/water_leak_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/model/water_leak_status_model.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WaterLeakBatchControlView extends StatelessWidget
|
||||
with HelperResponsiveLayout {
|
||||
final List<String> deviceIds;
|
||||
|
||||
const WaterLeakBatchControlView({Key? key, required this.deviceIds})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => WaterLeakBloc(deviceIds.first)
|
||||
..add(FetchWaterLeakBatchStatusEvent(deviceIds)),
|
||||
child: BlocBuilder<WaterLeakBloc, WaterLeakState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterLeakLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WaterLeakBatchStatusLoadedState) {
|
||||
return _buildStatusControls(context, state.status);
|
||||
} else if (state is WaterLeakErrorState) {
|
||||
return Center(child: Text('Error: ${state.message}'));
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusControls(
|
||||
BuildContext context, WaterLeakStatusModel status) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 140,
|
||||
child: FirmwareUpdateWidget(deviceId: deviceIds.first, version: 2)),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
SizedBox(
|
||||
width: 170,
|
||||
height: 140,
|
||||
child: FactoryResetWidget(
|
||||
callFactoryReset: () {
|
||||
context.read<WaterLeakBloc>().add(WaterLeakFactoryResetEvent(
|
||||
deviceId: deviceIds.first,
|
||||
factoryReset: FactoryResetModel(devicesUuid: deviceIds)));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/main_door_sensor/view/main_door_control_view.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_event.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/bloc/water_leak_state.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/water_leak/widgets/water_leak_notifi_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/table/report_table.dart';
|
||||
import 'package:syncrow_web/utils/helpers/responsice_layout_helper/responsive_layout_helper.dart';
|
||||
|
||||
class WaterLeakView extends StatelessWidget with HelperResponsiveLayout {
|
||||
final String deviceId;
|
||||
|
||||
const WaterLeakView({Key? key, required this.deviceId}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isExtraLarge = isExtraLargeScreenSize(context);
|
||||
final isLarge = isLargeScreenSize(context);
|
||||
final isMedium = isMediumScreenSize(context);
|
||||
return BlocProvider(
|
||||
create: (context) =>
|
||||
WaterLeakBloc(deviceId)..add(FetchWaterLeakStatusEvent(deviceId)),
|
||||
child: BlocBuilder<WaterLeakBloc, WaterLeakState>(
|
||||
builder: (context, state) {
|
||||
if (state is WaterLeakLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WaterLeakLoadedState) {
|
||||
return GridView(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: isExtraLarge || isLarge
|
||||
? 3
|
||||
: isMedium
|
||||
? 2
|
||||
: 1,
|
||||
mainAxisExtent: 140,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisSpacing: 12,
|
||||
),
|
||||
children: [
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: state.status.watersensorState == 'normal'
|
||||
? 'Normal'
|
||||
: 'Leak Detection',
|
||||
icon: state.status.watersensorState == 'normal'
|
||||
? Assets.waterLeakNormal
|
||||
: Assets.waterLeakDetected,
|
||||
onTap: () {},
|
||||
status: state.status.watersensorState == 'normal',
|
||||
textColor: state.status.watersensorState == 'normal'
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.red,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Records',
|
||||
icon: Assets.records,
|
||||
onTap: () {
|
||||
context
|
||||
.read<WaterLeakBloc>()
|
||||
.add(FetchWaterLeakReportsEvent(
|
||||
deviceId: deviceId,
|
||||
code: 'watersensor_state',
|
||||
from: DateTime.now()
|
||||
.subtract(const Duration(days: 30))
|
||||
.millisecondsSinceEpoch,
|
||||
to: DateTime.now().millisecondsSinceEpoch,
|
||||
));
|
||||
},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Automation Record',
|
||||
icon: Assets.automationRecords,
|
||||
onTap: () {},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
),
|
||||
IconNameStatusContainer(
|
||||
isFullIcon: false,
|
||||
name: 'Notifications\nSettings',
|
||||
icon: Assets.mainDoorNotifi,
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => const WaterLeakNotificationDialog(),
|
||||
);
|
||||
},
|
||||
status: false,
|
||||
textColor: ColorsManager.blackColor,
|
||||
paddingAmount: 14,
|
||||
),
|
||||
],
|
||||
);
|
||||
} else if (state is WaterLeakReportsLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is WaterLeakReportsLoadedState) {
|
||||
return ReportsTable(
|
||||
report: state.deviceReport,
|
||||
hideValueShowDescription: true,
|
||||
waterLeak: true,
|
||||
onRowTap: (index) {},
|
||||
onClose: () {
|
||||
context
|
||||
.read<WaterLeakBloc>()
|
||||
.add(FetchWaterLeakStatusEvent(deviceId));
|
||||
},
|
||||
);
|
||||
} else if (state is WaterLeakReportsFailedState) {
|
||||
return Center(child: Text('Error: ${state.error}'));
|
||||
} else if (state is WaterLeakErrorState) {
|
||||
return Center(child: Text('Error: ${state.message}'));
|
||||
} else {
|
||||
return const Center(child: Text('No data available'));
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|