g0&wNzEJY@g-&;Okd6
z7Wv)8d>-Vcbj{(WNmf85D@431X}LV&8mTD!=ew60`;P0)D=s=8uew8bowlr+HBDlN
zH*!Gckf`=hF^|vEc~R?+3vUTJpsKRWQn}=^joTCB1A<`^m0GZ_keHP7ZjojYk8Z23
z-X_fxc*kJxmM5z>=xN<74?^lU)ENYxB_33{GwPx#=zj(sy?FWW-|fEUs=
zKldjOzIH7(25xLX;*NOe9{CtLP9tsXp{a3}4^Ny3vmMRDSSOwc-N2}~t>wdZPQkLW
z2cyE_btV-S)RvJut?myqA>yh`Dr61m;>Nuv#ZmLX?_>-aEqo%hv%Xb=ZUc03W(3vF
zsT=MZ+IgqdZ}N9><59yW{$vimJe$TcBBX!z?}Ql=H?!3aT^y(9VKMon?6@XC8eO+Ue8kejj3hOi*#k0H1*Y
zw$uPQwe!pQ7{LK5rexB=Aqlff3`uEq?>Foss-Goq%c4Y@qN@{ecD0C%PO109hCM`e
z7jk+QrMb}?k>k-<>OvmQqReOl*7#p^^M2;hlZlKILQX_!Qx+Wt7L~6tNuu=Vqe-zi
z>N~=@FTJ~mGwu;RbjX^ID3S(f!N)l6YQiP=J_TKFuMLlJ{MCf3|Eq4w)NX(R4~#E;
z5_pLKc(nj{(V0iBAr?)Psg0YmC>dDPe_O3!`LGJqq7O2grYwpF7DdKqHl0kAgIW|+
zJU(gBMqp6~U{MvPoVO5*z6TYjEII-#DrgJjboxUr3M$r5S~LS#6b~%=*2Dp7QBbjb
z%A%;`%qB2L{af81KrIR?PFb`YSX8zK$N@uy%u!G=by8LKKvfw)Rba$W@Y~DOhE1u8
z^ix&U3sj}$lmlI8-&-fTOsR?jR0Xq5bP4SYhpGxxoLaR-u
zTR;;4e|W2V3{+L1;*_e$Kvjx0Ku)a!R8^p2#-yrxfT~7;s$w#mps4DRsm+>F6&0u|
zU|Ve#S|S;}F&QnHjNY7#-ja-#Nk+>iqqirc<&)7nlhKOF=-qvJn8lub`)83(%p&>D
z`X6)@1s1e9<->a*@E^FBsW=7yLI8h@_)Nu-9W`D
zo3;R(O4k56V2BX-gNn(MHeCg58j#XT?A!2uOvHXdK+Y>r5Le=lXnmFB)WZL)e?#L~
zwEcvUoL5SichXs>zR&g(?s8svVL!KnV!@Hw*(Ts^)}(Zc#fy
z#VL!91BIACV
zRs-aKAws|pDn6M+)lq<|B!DU~V(8j`RHim$3ROgas-LGSZG`{9T5Tuteq7ZFOb+h?
zEiGkh+1pVYH&sdmhbvMaKSR`8aN9W<2OEt>rRZM`uGe1~-p;tCds=
zC+OA3D)^L-JeSQl7<)!rK%wAp4q0Z@i;F7}BJY`g-I*VR@iozf4KNGq9{oWWCxs^L
zKr*v1-yehpH`9eJQ)lYg!to!3Y0`!HJz*Ah=LcbYZFFJs%)&Mie-IW-7gmzSEG+H^
zVZohrVMm#TX_CGQlP|DNMq=#nq9Aq&u>riG;~vsB4FCAuGg1F=^S>zeOn+q>68Y6m
z3$WcmNRlP~;`zMdSh4ciK{un7jiR6EyZCuy6Oi_CdYut6XZ`Z9s3){WbvAF`!53YB
z?8cKc)i{H*U$joY!KRlZ585;az0#7d!)nu{33AOAjfs>I)X=-N%sRM4t5JMW0~VW^
za3H~>vQB(9ex#TtO$n;~f<5?}Z@4&ySF8upkYda4-u0`r+pKoPDC(eu!kL)uC-Duf
zSc&u=HH(<#G%WyW3pu@m^mRiQt`}Fol6Jrn>hz+VFt1`^Qc>
zN*qYdTW4h*cqq2(U3l|ai#+wDTQ<`gge*$H=N!tO9h5cx(mxF`l3`
z%65!)ctt$>F(UCNm~g`ch*TuWjy)i-NDPuKAS$rksq6v4R$1}|R>??&9iE@5^NR3-
z&y03tS&V6nSCAxg_G3(;HOdb3QrPY*?8jJuMWsM-!vu)jI;<3XK=9*73fKd}91oejCF*ab6*pJbW)+k%h+u(B>*pCs3H)O&M6CmP{
zBpddCNJk>rgFXq{9mgIJY?UQnfJ{ab?C^3-od=-rW&}OUVoYNULz0fOA7d)5QFfqr
zz;=hRA7eQdl?ufTGa%}*4(tIThaV|t4~XOVT=pwHTV>tXe-A+4h(%>UI}bqr9s+t+
z#hAy4$M&)xqYJH3wxGAi=dxev2}ryP6K55=dhQh+b@W7JERjRhE2#
zMKTg@hv#PMJOF(QBj{NcV;Z9`l4QnqjAVMF?8XSgcKfm)V?Gv0W9Qy@)nQ@m0l|$Q
z$!8A;Gdz&SZl!0dto!;q0qCo+m#ngEK`c6jBvn<9m#tCe2dIYvRg#8#xv1Il>y;nUJ!5$D&_>offfUv>?Y3u;OR#}1icX0x?cuXr%
zziX&oJc^McfZv=YPVmn8MqXYujKpM)>c7E8GZuUo{sN0S_KkVPBhjHAa89H#6BDw-
z|10bp0sF4Ah2hOIf59Fxm}cjH5Xnr@pJqn>M*KBk$gJ4o(75<(2$5!0jG0LPq9jwH
z0Gv8ZBO?c(-$LViEZ8Fey(1&&Sr%hDS@}gtCLQ$C$;jV`Cj!v3Vvhjyi4f4UD#iqQ
zKns6Kl1T$S;Ly{?$N}hO>j2+l!5#tV&oY9ZWicku|Dq(54*Kb2rJN;2u7pGHOwKo1VbS+Yj}ddL|%%VJER|3yhA9rV-5$lr)Z0MN5yj{x)$
z5YV$K#sqpm3x7$HNdx_KGI9X=ZFPX}v0#q?^k*1XdX~kQK>v%9OgiYNlaUvROCtQ!
zyFsuP@YgP0J;taV_>BU}%Wvkl2$=dB{
z+pyjL3i}ok{jRf%TBim6B1BoCeOms17s;dp^0yBAw)3`um^G^qB{~_K+mce6X<(?P9$@>NhTfi)5*v$YW0H*ES4e43hjPK
zq87_yOrZZckxV-1r;}vTK|h_0yq&iqj~1u}Toe+%1E8;91U<`QOrXd7oJi($lT143
zr<0Kb&;y^IWrz}h9^%upEXD--pA*TXgMKw&vJ<~riP-<
z`h|mN`Jdl%R#UKK>mD+18WK|QOiVHoR`)H3lotGL(%yr=1x|5ULcbzH4
zY+uMaX}GE_X1mb8xyAoCxyAI^%a)Nl`wjj@h!ULV3_|j!r!kT_?Ie?)Y(AZgTq))$
zh|9BLj{x*fAqn-YiZLhD|C?J(2R&P5Is0|~MTioBzLOF3(;3N}c9Ka4{d6*N0D7>}
zvto}l&_h;wR>hb>|2yXvlhIde)&05;EaH44e$Y$%IH6yzE`n;wB{|D7n{VM6{u{fy
zYOX0SvBQI42gvkuetP?EiQegy1DandufJAP=MBRqm>$iFKH(<5OgR~euE&DxwKpHl
z9VOHY3M970sw>HhZql`Dz@ByMz7_E~T8EbaH~0{TXZ3wkQ$5-=R}teuiIj
zH6a#6KX65Uv(8~&U>tgW<=>d_-$K#ym`h(alRAW9!4~uVRw8c|fDS{wx8M<-Z}lucyNJ>jR*B>cIv*EBJk_L@in+6#i*(d
z2VNM&E3L!A*`v=4PXRZe*h~p
z74Y9H7@Zn$!VPXxu|XdYgl5{Qnf!Nf8428eVze`p|E`T60r#39OCNMf{Fh38U2T!L)7RCvK6gOh@F|78gP6MZa%S@62E4rcne$rr^VBlD1;D^{hK}h
zG3;@=dA46O-`8jr1XcvGql`;Cj(-1;$*23b5aeKnt)6%bw3BJ{63w(x0;r#0g!*sH
z9_dh@MxO20EC+Os1WXjVQBqX;v;Hyck#=@CojiMAKN&}3aTzMo9ooDQ8b`itUP%y>
z{htxJg2;aW^Sv+D1pN1MEE&A*!RXfk|J?zye*eDgk$x;RjXYb2w-@;D_IN=0AbuV2
z-$xn!cf#+>9_c}WY2?{regVLL2fifJ`7JnyHNP=?{Eq^nKaXc~DfbK6fru%HV?vK(
z(GSL&{tbx!zX=_KV|Py4+b^`2WSCCGAV)Kksg)2;3q3vjx6o9k>R;;#K%IvcG3|!2
zf_gn8)c@%L(Vs_YeImy|Y7K%G$AliU;&dnM8Pf><=>gH7M`?w1vnl+vC})k>dh+GH
zkOOYGt_JWS|4D(){{cVdEieZ8GpuMRI2}VWI+83Su+T6B!440ek7N@Y0U=t@Cgi}M
zDK^52Lo>xje$7t#HSem!vZSHlbS#(Akz^Txh3>83mK1nklHGAJp4KLWF#MRrM)%oR
zacHL4$S>I`u)BiX7?v~?IFgXeI#v-_CiHRRN5B)5kcA66CE&C+*-Z(S9GYoL{F0pl
zyDP}nW=TVFx}K2Tl~n|m34P$rNbpGIU%OHC94HK}O?Fd)C5L9362D}p_Ud{9|D7cb
zC5Yuhl1*7fV42VdxBz&@lJQWJX-WVuA3RXWW=gQ+&`dPum+aJiv0U)P4XeCdaQ;hR
zJpW}Gfn`D;tE6J4DUm^IliieH$)TB2omqv2|G)2)4lhU~n0(O`;(UM|wSkd1OWggVOzEEB3&B^5J4J%H9GJE*hd&`_xV
znw`?eq-7-&Bc{cFh!OaSmdAS5JMTq~-Dgw*2g8!22k$!`1
z8hJLzzXtw0tGry`)LG>Pe$g{=#
zdjI08DL8u{Wz66BjoBmp;By*zHg5q)BtXy~V}+cQfE|@_X=ll4p&tCdg;;a{AN3^W
zhXGJ$l>!T(Ucm_U-FH&GZgvo_5!t@)nM|!Y&8hQ5R=20~5qNcsS
zc_l_Hit!MK$Wsu7)3y75a98s`BXYo%{{1hvg8P%;{GDYAEVw@jS>*re0nwjFX+i!q
z@ZVXbzyklhm(hO*cUJ$=0nwjGX?1vEz<+0z0t@{207n1)pB@nXfzlGuEyGxwVx;mP
zLxux$PWR>R?F%}f7b~GUJWr@HMQLwC-NJG5*!i+0O5E;vkK|jjSLXR{^}Z6OuX{l9
zxOIr1v+9YMHGXRNn#W1oIs~+gu9&AF(coyf27I!OUoIx{rb$_)Bwxq%*IG)vLD+T0vBHQs1AeZ6VEnyv1}Zx0UreUsoc
z3fz_FWt6p0SEB)Y2$i5#d#cii7rZPr`H1d^=2nB7H?ACMh!z&Athc^oUWrZrVypS=
zVo*)w;cw6O{&nY<6@VA0CqGj9^eOoM^(XUNXJb`;ozDtH-cCa%5RHNoKSrgCEu%gE
zOaIul
z6;)1S7&|}pZO*^%JWE+jou7PXmrdtEMhETVqipOv`5jX>cAorN4I4Xy^F-R$RoU2i
z^0P2(?95J8o0R(ju)rA-?dzS44ZZ77;Iu8!zXx}IO
zdppA>KXuB88K|A7zM9I$&fq|i_5s$vw=-Cw!9{;|^O>EhZbiCE(jKyRntmoo6Wvy8gi@KMBJIq?2ElVPj|Q$*-X@Vus1ilOI-PW9O;Qj{d!!F_Z89
zva$2zOT&MyDs|m#u=-6s2FuuBq1e&y+W;M;lOF+P(|MM%peyO*c?M)*V*=^qS5E)B
z^Yl#zWUbIXn97J5sGY$vpcH$MP6n~r*jZ}wBd2Wa%uZF&VohL|L*l)_Qz(oLc3ANC
zGH%j1BpzI6U^l~9$zs|u9q=O{^#ii7F@Y4gy5M84Z0rouR>1ppY!)K|wi`Sr$Ii}J
zBp9L&8#{xn74Uo=J3F&e)d61gcxzbtGcxJJ@>j5tHU5`
zeEAyQr6h^2D?E1mu&5OFvT4g1obO;820b=0ZaX$KT;+l$_g9T#s3RRQz0_d>hB}0)
zpiXRxMo?anhdjh^C<^g&AwiLRqmh8~B#S(ubo*9dDE;Fx2po9;HU!6cjyK>Yi0V|*
zKot&63h$+G>!{D1#l^wFF_$BALC>Lbi@eCyGdVbfxj8r%g5SE@NjW*Y*;zSBx+3kW
z4UyXT&5J1;0&)B?(N<{(&Yx6Yw@y~`yc+4tIxU-zAw{PTm!?q_aZ#EQ?KN*-tcIDB
zJiU4{Q!C`>@T;rj%-Og>*mW*ZRIzWfwz7)rlJgEOr+Zhr2JhXeWN>QbdA`Kw%De_P
z%KhClEIwOv9Q8SN-(bg|$p`pv-~Y1j=IzcqCaM>2t}2;vDEg&XT$!-S$LFIJtCcRy
z^xb@9#8(V;K3`QrS;X7il%eCD6p@bCQHbePCf`M(Jof`8iE;+H
zmUAZOT{>^lx6CH$j^NrL4@s}SI*lEk52C%v?YmY#7D0Ija!EwRq#^3Wtmo}tpy0nE
za$VpoSEJCiD(xl3b5zP-Me17a%Q~@Q2Tu>?d7FQ+@Vq@MNnl2}iqDIzz~j$T_SeDX
z9q0C~@kA^hoLwb*ymnr6m??}y^wJCz2j8uoH8Ybs2nh#}ew}x#jFKN5du}%v6791u
zj&dx`HXSMRU|*5O33NyhkMbV{nU#{NdK)$3_RTAg(vx5EVD;KZi&yHld~z=+HIx<7
z6f3yd6pH`sksEANnjLgJS8YpfhmB!qqW9^U-UMZ*I@7wqeACwQFPmnXJ`$?=aI}>4
zT**q!P=7;8%eMFKB~jU7C-b^riimKYa21-Bw#t3}n&JmXt+b3Da~?L5KXs?K*|}l2
zSL~7VR(8E&wu6n*=^^cNK0fQ!;=cBxcwJ4(@^A`FMIp!<9md&uST@??UbVM{=t)+j8EXk`1qDNrCsB
zz-f$I5q9xaSIF02x4YVeeX`hj_G`PgYS-ssZ*$?Av$mZc&9Hwy;wDI*%{Y|~nl208
z{|5)hwOJe-JYXsrD>>LZTiH7!4BSpxIhoQXRJuW_R{rM26TwA>H=lk!x7em(X24p5
zNY8UAa{dQ#pBitC^wzI4*n9B$?lgYfahKyY1Mb*555un25B%>htFjU1nkz3LSd;E*
zyel{(a<^Wd82`+7%^YIO2J63E=a~O`;o&Q{#2VEuKjTbF_M10jt!CNLB@Tl|>!amt
z77n;?$qbaAeSWvdnU&^>9_Lh$LVTn7Zu?E=2j56FT7tikbxcdORO*Fi`n;s=
zyLkgVj;|;&bKwRFwoZ-8AW+nF)U^zaSR#yZY$}Tt_
zB2kq(P$n^|<@GY#E#S1isRmBJy%t_Py1K+GG@@!wf6F=P>Q&2MrE*1>>=au0;Wp)*
ziB9Ih6=fT&>|;;QB+R6IZmHvwKQ5+!TqZ~;HZ)vvg>pS-M5<%EQPH{a;O+XbbSo;R
z4E_hB-f`n+axMV9!@x4O80g*7!9vH;;gpk<*{M@BrHAXiwa0H>Fu|u(VSAG|fAg#~
zKjDI!KLnSpwjM1jc8))IYu4rA8z{2noU`G49vy;iJMUh5msg&WuDoA*Mel(Wqm9aY
zv`8gSmBd$#U=kJmgr%QxCAb*)@ray$a7++&ujry)Ag76ui;3UG9r4i`&w!Y!*wZL@~L10|$V^CPdZv6}+ITB}7*iM73VLpH@#?=C73
z(bZCML|LD9sM8}Jb&wuFujJMa@G>-SJzYyO&KZ!)9SRtcb9TKa#%Jd#_$K^a+oeBc
z#~=0jNohzBY5r=DoiATo1IbMoYNVp1N(zV@nWQ$@#OMqt32ERuE*`7a929t@T!1
z9MD!4w{m>VTnp|25&RdauNLE=Z#uw8-+tzSzky8$;b>*&^sU(B-ZXa`VNDD8=ch9`
zI0Swy=HlRJYwqA+OS7&dtzyEBQk$2nd3U3gdvY_2({Ad$x}zyxhSh}c9%{{7C((r8
zPsx?N@@Dr;Q_BaJTwm|m6@h!T2c^@j=(_Nr?s;Jc?@mH!W?H#3se>yJSQmq{?5Lh
zgL=6_O$wHr2=hgF=`9P>&;=T%Lm6T2#Y>bMuuDs+7huWW8M8-
zQUmMzkEy9-Nqiz0hI7o>U>(pfdMGY~AciAaxVs8hgs1wr!|zC}jx)vABk$)XB~>Vj
zAKQaB@7>5}qaB}Ea`(vUvqqBdv@D+%ZK+Y%Qp6*7-M{3OoA=THudx0P^NgF8T<%C6
z>=;ryo}9{+HvbUU8H(gZiZK82DI-wQlI|BL`__5^~$JRgQ&;K
z5S&LuOH`+K$IV~e%=hl(eJ@NddPSN%A!VPOqsj5hCmdOk*S-IWQ;b^bk
zF~zxSeXz54n7ql~Vw_eZG+MK2MX+GS{RQ_U_6Js|$6tWe3oLBptvM*X_>6B+?INEc
z5%WjR&alF&M@vhdSDW=vTFkEO{XAwj_;4>z;`&ADb7iun4tB3G<~`6d66%}z(97=T
zt(C`HTo-8Im!Vt(C01MWEPV-+_(D`NxH%BN=9!O)g0N7XgdJJ4S4^SzW+3)+-}TBK
z$(dnGtv#~!x7Duwql+bRN%;4pqd9}<7%xwB^
zX@Af*2iniqwdQM`$<@XNm;I_0{ObW=nfdf`U;kM5tqy&$`MbtwtJ&lat+B1Z(BF2W
zzpDmj@zf8knh~f5aPfB)7~Aoftl)go3TgF2waIa1+Yy8lIXO7a2u`{sUt8kfi2Sh{
zy|OfIMg89HqyY2u?I;*1x*aHH$@kjeZ-37DVSE-2j#iA-nQgJ?TXotvE9QQ~ztc%8
zD`(pJ&uEji7i&J8xx>M6iEB2;@^5u%e<3l|rHf!W04T@#F6dz-y$9bJ53~bNpYD6r
z{%DpS9#jC-Ir%QYI_3xEO}Q%HYcuPP(d2@$^q#YZGIZno-ss*RtNpm}K#)CYdFU?l
vU5@GFNyS0;obLmelmK1szDrmZ|IPScw{jhIPWohB3})&pFeTg*XvO~z%H;CA
literal 0
HcmV?d00001
diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts
index d24c93d0197..fdaf802d4b7 100644
--- a/cypress/pageobject/Asset/AssetCreation.ts
+++ b/cypress/pageobject/Asset/AssetCreation.ts
@@ -1,4 +1,5 @@
// assetPage.ts
+import { cy, expect } from "local-cypress";
export class AssetPage {
createAsset() {
@@ -74,6 +75,14 @@ export class AssetPage {
cy.get("[data-testid=asset-notes-input] textarea").type(notes);
}
+ interceptAssetCreation() {
+ cy.intercept("POST", "**/api/v1/asset/").as("createAsset");
+ }
+
+ verifyAssetCreation() {
+ cy.wait("@createAsset").its("response.statusCode").should("eq", 201);
+ }
+
clickCreateAsset() {
cy.get("#submit").contains("Create Asset").click();
}
@@ -87,7 +96,9 @@ export class AssetPage {
}
openCreatedAsset() {
+ cy.intercept("GET", "**/api/v1/asset/**").as("getAsset");
cy.get("[data-testid=created-asset-list]").first().click();
+ cy.wait("@getAsset").its("response.statusCode").should("eq", 200);
}
editAssetDetails(
@@ -97,7 +108,8 @@ export class AssetPage {
manufacturer: string,
supportName: string,
vendorName: string,
- notes: string
+ notes: string,
+ lastServicedOn: string
) {
cy.get("[data-testid=asset-update-button]").click();
cy.get("[data-testid=asset-name-input] input").clear().type(name);
@@ -114,18 +126,71 @@ export class AssetPage {
cy.get("[data-testid=asset-vendor-name-input] input")
.clear()
.type(vendorName);
+ cy.get(
+ "[data-testid=asset-last-serviced-on-input] input[type='text']"
+ ).click();
+ cy.get("#date-input").click().type(lastServicedOn);
cy.get("[data-testid=asset-notes-input] textarea").clear().type(notes);
}
+ configureAsset(
+ hostName: string,
+ localIp: string,
+ userName: string,
+ password: string,
+ streamUuid: string
+ ) {
+ cy.get("[data-testid=asset-configure-button]").click();
+ cy.get("[name=middleware_hostname]").type(hostName);
+ cy.get("[name=camera_address]").type(localIp);
+ cy.get("[name=username]").type(userName);
+ cy.get("[name=password]").type(password);
+ cy.get("[name=stream_uuid]").type(streamUuid);
+ }
+
+ configureVitalAsset(hostName: string, localIp: string) {
+ cy.get("[data-testid=asset-configure-button]").click();
+ cy.get("#middlewareHostname").type(hostName);
+ cy.get("#localipAddress").type(localIp);
+ }
+
+ spyAssetConfigureApi() {
+ cy.intercept(/\/api\/v1\/asset/).as("asset");
+ }
+
+ verifyAssetConfiguration(statusCode: number) {
+ cy.wait("@asset").then((interception) => {
+ expect(interception.response.statusCode).to.equal(statusCode);
+ });
+ }
+
+ clickConfigureAsset() {
+ cy.get("#submit").contains("Set Configuration").click();
+ }
+
+ clickConfigureVital() {
+ cy.intercept("PATCH", "**/api/v1/asset/**").as("postConfiguration");
+ cy.get("#submit").contains("Save Configuration").click();
+ cy.wait("@postConfiguration").its("response.statusCode").should("eq", 200);
+ }
+
clickUpdateAsset() {
cy.get("#submit").contains("Update").click();
}
+ interceptDeleteAssetApi() {
+ cy.intercept("DELETE", "**/api/v1/asset/**").as("deleteAsset");
+ }
+
deleteAsset() {
cy.get("[data-testid=asset-delete-button]").click();
cy.get("#submit").contains("Confirm").click();
}
+ verifyDeleteStatus() {
+ cy.wait("@deleteAsset").its("response.statusCode").should("eq", 204);
+ }
+
verifyEmptyAssetNameError() {
cy.get("[data-testid=asset-name-input] span").should(
"contain",
@@ -160,4 +225,34 @@ export class AssetPage {
"Please enter valid phone number"
);
}
+
+ selectImportFacility(facilityName: string) {
+ cy.get("input[name='facilities']")
+ .type(facilityName)
+ .then(() => {
+ cy.get("[role='option']").contains(facilityName).click();
+ });
+ }
+
+ selectImportOption() {
+ cy.get("[data-testid=import-asset-button]").click();
+ cy.get(".import-assets-button").click();
+ }
+
+ importAssetFile() {
+ cy.get("[data-testid=import-asset-file]")
+ .selectFile("cypress/fixtures/sampleAsset.xlsx", { force: true })
+ .wait(100);
+ }
+
+ selectImportLocation(locationName: string) {
+ cy.get("[data-testid=select-import-location]").click();
+ cy.get("li[role=option]").contains(locationName).click();
+ }
+
+ clickImportAsset() {
+ cy.intercept("POST", "**/api/v1/asset/").as("importAsset");
+ cy.get("#submit").contains("Import").click();
+ cy.wait("@importAsset").its("response.statusCode").should("eq", 201);
+ }
}
diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts
new file mode 100644
index 00000000000..315ef455aaa
--- /dev/null
+++ b/cypress/pageobject/Patient/PatientConsultation.ts
@@ -0,0 +1,108 @@
+export class PatientConsultationPage {
+ selectConsultationStatus(status: string) {
+ cy.get("#consultation_status")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(status).click();
+ });
+ }
+
+ selectSymptoms(symptoms: string) {
+ cy.get("#symptoms")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(symptoms).click();
+ });
+ }
+
+ fillIllnessHistory(history: string) {
+ cy.get("#history_of_present_illness").scrollIntoView;
+ cy.get("#history_of_present_illness").should("be.visible");
+ cy.get("#history_of_present_illness").click().type(history);
+ }
+
+ enterConsultationDetails(
+ examinationDetails: string,
+ weight: string,
+ height: string,
+ ipNumber: string,
+ consulationNotes: string,
+ verificationBy: string
+ ) {
+ cy.get("#symptoms").click();
+ cy.get("#examination_details").click().type(examinationDetails);
+ cy.get("#weight").click().type(height);
+ cy.get("#height").click().type(weight);
+ cy.get("#patient_no").type(ipNumber);
+ cy.intercept("GET", "**/icd/**").as("getIcdResults");
+ cy.get(
+ "#icd11_diagnoses_object input[placeholder='Select'][role='combobox']"
+ )
+ .click()
+ .type("1A");
+ cy.get("#icd11_diagnoses_object [role='option']")
+ .contains("1A03 Intestinal infections due to Escherichia coli")
+ .click();
+ cy.get("label[for='icd11_diagnoses_object']").click();
+ cy.wait("@getIcdResults").its("response.statusCode").should("eq", 200);
+
+ cy.get("#icd11_principal_diagnosis [role='combobox']").click().type("1A");
+ cy.get("#icd11_principal_diagnosis [role='option']")
+ .contains("1A03 Intestinal infections due to Escherichia coli")
+ .click();
+
+ cy.get("#consultation_notes").click().type(consulationNotes);
+ cy.get("#verified_by").click().type(verificationBy);
+ }
+
+ submitConsultation() {
+ cy.get("#submit").click();
+ }
+
+ clickAddPrescription() {
+ cy.contains("button", "Add Prescription Medication")
+ .should("be.visible")
+ .click();
+ }
+
+ interceptMediaBase() {
+ cy.intercept("GET", "**/api/v1/medibase/**").as("getMediaBase");
+ }
+
+ prescribeMedicine() {
+ cy.get("div#medicine_object input[placeholder='Select'][role='combobox']")
+ .click()
+ .type("dolo{enter}");
+ }
+
+ selectMedicinebox() {
+ cy.get(
+ "div#medicine_object input[placeholder='Select'][role='combobox']"
+ ).click();
+ }
+
+ waitForMediabaseStatusCode() {
+ cy.wait("@getMediaBase").its("response.statusCode").should("eq", 200);
+ }
+
+ enterDosage(doseAmount: string) {
+ cy.get("#dosage").type(doseAmount, { force: true });
+ }
+
+ selectDosageFrequency(frequency: string) {
+ cy.get("#frequency")
+ .click()
+ .then(() => {
+ cy.get("div#frequency [role='option']").contains(frequency).click();
+ });
+ }
+
+ submitPrescriptionAndReturn() {
+ cy.intercept("POST", "**/api/v1/consultation/*/prescriptions/").as(
+ "submitPrescription"
+ );
+ cy.get("button#submit").should("be.visible").click();
+ cy.get("[data-testid='return-to-patient-dashboard']").click();
+ cy.wait("@submitPrescription").its("response.statusCode").should("eq", 201);
+ }
+}
diff --git a/cypress/pageobject/Patient/PatientCreation.ts b/cypress/pageobject/Patient/PatientCreation.ts
new file mode 100644
index 00000000000..9b8df4a287e
--- /dev/null
+++ b/cypress/pageobject/Patient/PatientCreation.ts
@@ -0,0 +1,127 @@
+// PatientPage.ts
+
+let patient_url = "";
+
+export class PatientPage {
+ createPatient() {
+ cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities");
+ cy.get("#add-patient-details").should("be.visible");
+ cy.get("#add-patient-details").click();
+ cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
+ }
+
+ selectFacility(facilityName: string) {
+ cy.get("input[name='facilities']")
+ .type(facilityName)
+ .then(() => {
+ cy.get("[role='option']").first().click();
+ });
+ cy.get("button").should("contain", "Select");
+ cy.get("button").get("#submit").click();
+ }
+
+ interceptCreatePatientAPI() {
+ cy.intercept("GET", "**/facility/*/patient/**").as("createPatient");
+ }
+
+ verifyCreatedPatientResponse() {
+ cy.wait("@createPatient").its("response.statusCode").should("eq", 200);
+ }
+
+ enterPatientDetails(
+ phoneNumber: string,
+ emergencyPhoneNumber: string,
+ patientName: string,
+ gender: string,
+ address: string,
+ pincode: string,
+ wardName: string,
+ bloodGroup: string,
+ dateOfBirth: string
+ ) {
+ cy.get("#phone_number-div").type(phoneNumber);
+ cy.get("#emergency_phone_number-div").type(emergencyPhoneNumber);
+ cy.get("#date_of_birth").should("be.visible").click();
+ cy.get("#date-input").click().type(dateOfBirth);
+ cy.get("[data-testid=name] input").type(patientName);
+ cy.get("[data-testid=Gender] button")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(gender).click();
+ });
+ cy.get("[data-testid=current-address] textarea").type(address);
+ cy.get("[data-testid=permanent-address] input").check();
+ cy.get("#pincode").type(pincode);
+ cy.get("[data-testid=localbody] button")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").first().click();
+ });
+ cy.get("[data-testid=ward-respective-lsgi] button")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(wardName).click();
+ });
+ cy.get("[name=medical_history_check_1]").check();
+ cy.get("[data-testid=blood-group] button")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(bloodGroup).click();
+ });
+ }
+
+ clickCreatePatient() {
+ cy.intercept("POST", "**/api/v1/patient/").as("createPatient");
+ cy.get("button[data-testid='submit-button']").click();
+ cy.wait("@createPatient").its("response.statusCode").should("eq", 201);
+ }
+
+ verifyPatientIsCreated() {
+ cy.get("h2").should("contain", "Create Consultation");
+ cy.url().should("include", "/patient");
+ }
+
+ saveCreatedPatientUrl() {
+ cy.url().then((url) => {
+ cy.log(url);
+ patient_url = url.split("/").slice(0, -1).join("/");
+ cy.log(patient_url);
+ });
+ }
+
+ visitCreatedPatient() {
+ cy.awaitUrl(patient_url);
+ }
+
+ verifyPatientDetails(
+ age: number,
+ patientName: string,
+ phoneNumber: string,
+ emergencyPhoneNumber: string,
+ yearOfBirth: string,
+ bloodGroup: string
+ ) {
+ cy.url().should("include", "/facility/");
+ cy.get("[data-testid=patient-dashboard]").should("contain", age);
+ cy.get("[data-testid=patient-dashboard]").should("contain", patientName);
+ cy.get("[data-testid=patient-dashboard]").should("contain", phoneNumber);
+ cy.get("[data-testid=patient-dashboard]").should(
+ "contain",
+ emergencyPhoneNumber
+ );
+ cy.get("[data-testid=patient-dashboard]").should("contain", yearOfBirth);
+ cy.get("[data-testid=patient-dashboard]").should("contain", bloodGroup);
+ }
+
+ visitUpdatePatientUrl() {
+ cy.awaitUrl(patient_url + "/update");
+ }
+
+ interceptFacilities() {
+ cy.intercept("GET", "**/facility/*/patient/**").as("getFacilities");
+ }
+
+ verifyStatusCode() {
+ cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
+ }
+}
diff --git a/cypress/pageobject/Patient/PatientUpdate.ts b/cypress/pageobject/Patient/PatientUpdate.ts
new file mode 100644
index 00000000000..b26ef678679
--- /dev/null
+++ b/cypress/pageobject/Patient/PatientUpdate.ts
@@ -0,0 +1,97 @@
+let patient_url = "";
+
+export class UpdatePatientPage {
+ enterPatientDetails(
+ patientName: string,
+ bloodGroup: string,
+ phoneNumber: string,
+ emergencyPhoneNumber: string,
+ address: string,
+ currentHealthCondition: string,
+ ongoingMedication: string,
+ allergies: string,
+ medicalHistory: string[],
+ subscriberId: string,
+ policyId: string,
+ insuranceId: string,
+ insuranceName: string
+ ) {
+ cy.wait(10000);
+ cy.get("#address").scrollIntoView();
+ cy.get("#address").should("be.visible");
+ cy.get("#address").type(address);
+ cy.get("[data-testid=name] input").clear();
+ cy.get("[data-testid=name] input").type(patientName);
+ cy.get("#phone_number-div").clear();
+ cy.get("#phone_number-div").type("+91").type(phoneNumber);
+ cy.get("#emergency_phone_number-div").clear();
+ cy.get("#emergency_phone_number-div")
+ .type("+91")
+ .type(emergencyPhoneNumber);
+ cy.get("#present_health").type(currentHealthCondition);
+ cy.get("#ongoing_medication").type(ongoingMedication);
+ cy.get("#allergies").type(allergies);
+ cy.get("[name=medical_history_check_1]").uncheck();
+ cy.get("[name=medical_history_check_2]").check();
+ cy.get("#medical_history_2").type(medicalHistory[0]);
+ cy.get("[name=medical_history_check_3]").check();
+ cy.get("#medical_history_3").type(medicalHistory[1]);
+ cy.get("button").get("[data-testid=add-insurance-button]").click();
+ cy.get("#subscriber_id").type(subscriberId);
+ cy.get("#policy_id").type(policyId);
+ cy.get("#insurer_id").type(insuranceId);
+ cy.get("#insurer_name").type(insuranceName);
+ cy.get("[data-testid=blood-group] button")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains(bloodGroup).click();
+ });
+ }
+
+ clickUpdatePatient() {
+ cy.intercept("PUT", "**/api/v1/patient/**").as("updatePatient");
+ cy.get("button").get("[data-testid=submit-button]").click();
+ cy.wait("@updatePatient").its("response.statusCode").should("eq", 200);
+ }
+
+ verifyPatientUpdated() {
+ cy.url().should("include", "/patient");
+ }
+
+ saveUpdatedPatientUrl() {
+ cy.url().then((url) => {
+ cy.log(url);
+ patient_url = url.split("/").slice(0, -1).join("/");
+ cy.log(patient_url);
+ });
+ }
+
+ visitUpdatedPatient() {
+ cy.awaitUrl(patient_url);
+ }
+
+ verifyPatientDetails(
+ patientName: string,
+ phoneNumber: string,
+ presentHealth: string,
+ ongoingMedication: string,
+ allergies: string
+ ) {
+ cy.url().should("include", "/facility/");
+ cy.get("[data-testid=patient-dashboard]").should("contain", patientName);
+ cy.get("[data-testid=patient-dashboard]").should("contain", phoneNumber);
+ cy.get("[data-testid=patient-present-health]").should(
+ "contain",
+ presentHealth
+ );
+ cy.get("[data-testid=patient-ongoing-medication]").should(
+ "contain",
+ ongoingMedication
+ );
+ cy.get("[data-testid=patient-allergies]").should("contain", allergies);
+ }
+
+ visitConsultationPage() {
+ cy.visit(patient_url + "/consultation");
+ }
+}
diff --git a/cypress/pageobject/constants.ts b/cypress/pageobject/constants.ts
new file mode 100644
index 00000000000..72e0d31c662
--- /dev/null
+++ b/cypress/pageobject/constants.ts
@@ -0,0 +1,4 @@
+export const phone_number =
+ "9" + Math.floor(100000000 + Math.random() * 900000000);
+export const emergency_phone_number =
+ "9" + Math.floor(100000000 + Math.random() * 900000000);
diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx
index b5b0110096a..548df2901ec 100644
--- a/src/Components/Assets/AssetImportModal.tsx
+++ b/src/Components/Assets/AssetImportModal.tsx
@@ -220,7 +220,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
Select location for import *
-
+
{
Upload a file
{
)
}
id="configure-asset"
+ data-testid="asset-configure-button"
authorizeFor={NonReadOnlyUsers}
>
diff --git a/src/Components/Assets/AssetsList.tsx b/src/Components/Assets/AssetsList.tsx
index 5b820f93e29..6183a47783c 100644
--- a/src/Components/Assets/AssetsList.tsx
+++ b/src/Components/Assets/AssetsList.tsx
@@ -310,14 +310,16 @@ const AssetsList = () => {
options={
<>
{authorizedForImportExport && (
-
+
,
+ icon: (
+
+ ),
onClick: () => setImportAssetModalOpen(true),
},
},
diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx
index 2565bce64ba..34e5ed1cb01 100644
--- a/src/Components/Patient/PatientHome.tsx
+++ b/src/Components/Patient/PatientHome.tsx
@@ -1050,7 +1050,10 @@ export const PatientHome = (props: any) => {
Present Health
-
+
{patientData.present_health}
@@ -1060,7 +1063,10 @@ export const PatientHome = (props: any) => {
Ongoing Medications
-
+
{patientData.ongoing_medication}
@@ -1070,7 +1076,10 @@ export const PatientHome = (props: any) => {
Allergies
-
+
{patientData.allergies}
From e1f00fa423e3ac74ce63ecd4eed2a12b2cc5e886 Mon Sep 17 00:00:00 2001
From: Shivam Jha <86483059+ShivamJhaa@users.noreply.github.com>
Date: Thu, 7 Sep 2023 17:45:25 +0530
Subject: [PATCH 15/50] Added test for resource, facility and migrated them to
POM approach (#6106)
* lints
* Added test for resource and migrated it to POM
* trying a fix
* nits
* backend change
* Reverted useless changes
* fix test
* Fixed failing test
* Added api verification
* Diagnosis: Adds field Principal Diagnosis (#6218)
* adds field principle diagnosis
* show in consultation details
* fix typo
* fix cypress
* try adding wait
* fix tests
* open pdf file preview in new tab (#6226)
* Changed weekly to average weekly for working hours (#6228)
* Added padding to count block on patients page
* fixed date format in asset manage page
* Merged configure facility and configure health facility in one page
* removed commented code
* changed weekly to average weekly for working hours
* Resolved comments
---------
Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com>
Co-authored-by: Rithvik Nishad
Co-authored-by: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Co-authored-by: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com>
---
cypress/e2e/facility_spec/facility.cy.ts | 41 ++++++-
cypress/e2e/facility_spec/inventory.cy.ts | 28 +++--
cypress/e2e/resource_spec/resources.cy.ts | 70 +++++++-----
.../pageobject/Facility/FacilityCreation.ts | 106 ++++++++++++++++++
cypress/pageobject/Login/LoginPage.ts | 2 +
cypress/pageobject/Resource/ResourcePage.ts | 83 ++++++++++++++
src/Components/Common/DateInputV2.tsx | 4 +-
src/Components/Facility/FacilityHome.tsx | 2 +
src/Components/Resource/ListView.tsx | 1 +
src/Components/Resource/ResourceBoard.tsx | 1 +
src/Components/Resource/ResourceDetails.tsx | 1 +
src/index.tsx | 2 +-
12 files changed, 291 insertions(+), 50 deletions(-)
create mode 100644 cypress/pageobject/Resource/ResourcePage.ts
diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts
index f2e555bb58a..f8fe04b4f30 100644
--- a/cypress/e2e/facility_spec/facility.cy.ts
+++ b/cypress/e2e/facility_spec/facility.cy.ts
@@ -1,12 +1,16 @@
// FacilityCreation
+import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress";
import FacilityPage from "../../pageobject/Facility/FacilityCreation";
+import LoginPage from "../../pageobject/Login/LoginPage";
describe("Facility Creation", () => {
let facilityUrl: string;
const facilityPage = new FacilityPage();
+ const loginPage = new LoginPage();
+ const phone_number = "9999999999";
before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
+ loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
});
@@ -51,6 +55,7 @@ describe("Facility Creation", () => {
facilityPage.visitUpdateFacilityPage(facilityUrl);
facilityPage.clickManageFacilityDropdown();
facilityPage.clickUpdateFacilityOption();
+ facilityPage.clickUpdateFacilityType();
facilityPage.fillFacilityName("cypress facility updated");
facilityPage.fillAddress("Cypress Facility Updated Address");
facilityPage.fillOxygenCapacity("100");
@@ -61,6 +66,40 @@ describe("Facility Creation", () => {
cy.url().should("not.include", "/update");
});
+ it("Configure the existing facility", () => {
+ facilityPage.visitUpdateFacilityPage(facilityUrl);
+ facilityPage.clickManageFacilityDropdown();
+ facilityPage.clickConfigureFacilityOption();
+ facilityPage.fillMiddleWareAddress("dev_middleware.coronasafe.live");
+ facilityPage.clickupdateMiddleWare();
+ facilityPage.verifySuccessNotification("Facility updated successfully");
+ });
+
+ it("Create a resource request", () => {
+ facilityPage.visitUpdateFacilityPage(facilityUrl);
+ facilityPage.clickManageFacilityDropdown();
+ facilityPage.clickResourceRequestOption();
+ facilityPage.fillResourceRequestDetails(
+ "Test User",
+ phone_number,
+ "cypress",
+ "Test title",
+ "10",
+ "Test description"
+ );
+ facilityPage.clickSubmitRequestButton();
+ facilityPage.verifySuccessNotification(
+ "Resource request created successfully"
+ );
+ });
+
+ it("Delete a facility", () => {
+ facilityPage.visitUpdateFacilityPage(facilityUrl);
+ facilityPage.clickManageFacilityDropdown();
+ facilityPage.clickDeleteFacilityOption();
+ facilityPage.confirmDeleteFacility();
+ });
+
afterEach(() => {
cy.saveLocalStorage();
});
diff --git a/cypress/e2e/facility_spec/inventory.cy.ts b/cypress/e2e/facility_spec/inventory.cy.ts
index 91c6d36466f..c28035dd885 100644
--- a/cypress/e2e/facility_spec/inventory.cy.ts
+++ b/cypress/e2e/facility_spec/inventory.cy.ts
@@ -1,8 +1,13 @@
import { cy, describe, before, beforeEach, it, afterEach } from "local-cypress";
+import FacilityPage from "../../pageobject/Facility/FacilityCreation";
+import LoginPage from "../../pageobject/Login/LoginPage";
describe("Inventory Management Section", () => {
+ const facilityPage = new FacilityPage();
+ const loginPage = new LoginPage();
+
before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
+ loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
});
@@ -10,23 +15,16 @@ describe("Inventory Management Section", () => {
cy.restoreLocalStorage();
cy.awaitUrl("/");
cy.viewport(1280, 720);
- cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities");
- cy.get("[id='facility-details']").first().click();
- cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
- cy.get("#manage-facility-dropdown button").should("be.visible");
- cy.get("[id='manage-facility-dropdown']").scrollIntoView().click();
- cy.get("[id=inventory-management]").click();
});
it("Adds Inventory", () => {
- cy.contains("Manage Inventory").click();
- cy.get("div#id").click();
- cy.get("div#id ul li").contains("Liquid Oxygen").click();
- cy.get("div#isIncoming").click();
- cy.get("div#isIncoming ul li").contains("Add Stock").click();
- cy.get("[name='quantity']").type("120");
- cy.get("button").contains("Add/Update Inventory").click();
- cy.verifyNotification("Inventory created successfully");
+ facilityPage.visitAlreadyCreatedFacility();
+ facilityPage.clickManageFacilityDropdown();
+ facilityPage.clickInventoryManagementOption();
+ facilityPage.clickManageInventory();
+ facilityPage.fillInventoryDetails("Liquid Oxygen", "Add Stock", "120");
+ facilityPage.clickAddInventory();
+ facilityPage.verifySuccessNotification("Inventory created successfully");
});
afterEach(() => {
diff --git a/cypress/e2e/resource_spec/resources.cy.ts b/cypress/e2e/resource_spec/resources.cy.ts
index 099e782277a..b3393d0942b 100644
--- a/cypress/e2e/resource_spec/resources.cy.ts
+++ b/cypress/e2e/resource_spec/resources.cy.ts
@@ -1,8 +1,13 @@
import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress";
+import LoginPage from "../../pageobject/Login/LoginPage";
+import ResourcePage from "../../pageobject/Resource/ResourcePage";
describe("Resource Page", () => {
+ const loginPage = new LoginPage();
+ const resourcePage = new ResourcePage();
+
before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
+ loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
});
@@ -11,36 +16,39 @@ describe("Resource Page", () => {
cy.awaitUrl("/resource");
});
- it("checks if all download button works", () => {
- cy.get("svg.care-svg-icon__baseline.care-l-export").each(($button) => {
- cy.intercept(/\/api\/v1\/resource/).as("resource_download");
- cy.wrap($button).click({ force: true });
- cy.wait("@resource_download").then((interception) => {
- expect(interception.response.statusCode).to.equal(200);
- });
- });
- });
-
- it("switch between active/completed", () => {
- cy.intercept(/\/api\/v1\/resource/).as("resource");
- cy.contains("Completed").click();
- cy.wait("@resource").then((interception) => {
- expect(interception.response.statusCode).to.equal(200);
- });
- cy.contains("Active").should("have.class", "text-primary-500");
- cy.contains("Completed").should("have.class", "text-white");
- cy.intercept(/\/api\/v1\/resource/).as("resource");
- cy.contains("Active").click();
- cy.wait("@resource").then((interception) => {
- expect(interception.response.statusCode).to.equal(200);
- });
- cy.contains("Active").should("have.class", "text-white");
- cy.contains("Completed").should("have.class", "text-primary-500");
- });
-
- it("switch between list view and board view", () => {
- cy.contains("List View").click();
- cy.contains("Board View").click();
+ it("Checks if all download button works", () => {
+ resourcePage.verifyDownloadButtonWorks();
+ });
+
+ it("Switch between active/completed", () => {
+ resourcePage.spyResourceApi();
+ resourcePage.clickCompletedResources();
+ resourcePage.verifyCompletedResources();
+ resourcePage.spyResourceApi();
+ resourcePage.clickActiveResources();
+ resourcePage.verifyActiveResources();
+ });
+
+ it("Switch between list view and board view", () => {
+ resourcePage.clickListViewButton();
+ resourcePage.clickBoardViewButton();
+ });
+
+ it("Update the status of resource", () => {
+ resourcePage.openAlreadyCreatedResource();
+ resourcePage.clickUpdateStatus();
+ resourcePage.updateStatus("APPROVED");
+ resourcePage.clickSubmitButton();
+ resourcePage.verifySuccessNotification(
+ "Resource request updated successfully"
+ );
+ });
+
+ it("Post comment for a resource", () => {
+ resourcePage.openAlreadyCreatedResource();
+ resourcePage.addCommentForResource("Test comment");
+ resourcePage.clickPostCommentButton();
+ resourcePage.verifySuccessNotification("Comment added successfully");
});
afterEach(() => {
diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts
index ab958a49810..84b793b3949 100644
--- a/cypress/pageobject/Facility/FacilityCreation.ts
+++ b/cypress/pageobject/Facility/FacilityCreation.ts
@@ -1,7 +1,13 @@
// FacilityPage.ts
+import { cy } from "local-cypress";
+
class FacilityPage {
visitCreateFacilityPage() {
+ cy.intercept("GET", "**/facility/create").as("getCreateFacilities");
cy.visit("/facility/create");
+ cy.wait("@getCreateFacilities")
+ .its("response.statusCode")
+ .should("eq", 200);
}
visitUpdateFacilityPage(url: string) {
@@ -11,6 +17,14 @@ class FacilityPage {
cy.get("#manage-facility-dropdown button").should("be.visible");
}
+ clickUpdateFacilityType() {
+ cy.get("#facility_type")
+ .click()
+ .then(() => {
+ cy.get("[role='option']").contains("Request Approving Center").click();
+ });
+ }
+
fillFacilityName(name: string) {
cy.get("#name").clear().type(name);
}
@@ -99,6 +113,28 @@ class FacilityPage {
cy.get("#update-facility").contains("Update Facility").click();
}
+ clickConfigureFacilityOption() {
+ cy.get("#configure-facility").contains("Configure Facility").click();
+ }
+
+ clickInventoryManagementOption() {
+ cy.get("[id=inventory-management]").click();
+ }
+
+ clickResourceRequestOption() {
+ cy.get("#resource-request").contains("Resource Request").click();
+ }
+
+ clickDeleteFacilityOption() {
+ cy.get("#delete-facility").contains("Delete Facility").click();
+ }
+
+ confirmDeleteFacility() {
+ cy.intercept("DELETE", "**/api/v1/facility/**").as("deleteFacility");
+ cy.get("#submit").contains("Delete").click();
+ cy.wait("@deleteFacility").its("response.statusCode").should("eq", 403);
+ }
+
selectLocation(location: string) {
cy.get("span > svg.care-svg-icon__baseline.care-l-map-marker").click();
cy.intercept("https://maps.googleapis.com/maps/api/mapsjs/*").as("mapApi");
@@ -106,6 +142,76 @@ class FacilityPage {
cy.get("input#pac-input").type(location).type("{enter}");
cy.get("div#map-close").click();
}
+
+ fillMiddleWareAddress(url: string) {
+ cy.get("#middleware_address").type(url);
+ }
+
+ clickupdateMiddleWare() {
+ cy.intercept("PATCH", "**/api/v1/facility/**").as("updateMiddleWare");
+ cy.get("button#submit").first().click();
+ cy.wait("@updateMiddleWare").its("response.statusCode").should("eq", 200);
+ }
+
+ verifySuccessNotification(message: string) {
+ cy.verifyNotification(message);
+ }
+
+ visitAlreadyCreatedFacility() {
+ cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities");
+ cy.get("[id='facility-details']").first().click();
+ cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
+ cy.get("#manage-facility-dropdown button").should("be.visible");
+ cy.get("[id=manage-facility-dropdown]").scrollIntoView().click();
+ }
+
+ clickManageInventory() {
+ cy.contains("Manage Inventory").click();
+ }
+
+ fillInventoryDetails(name: string, status: string, quantity: string) {
+ cy.get("div#id").click();
+ cy.get("div#id ul li").contains(name).click();
+ cy.get("div#isIncoming").click();
+ cy.get("div#isIncoming ul li").contains(status).click();
+ cy.get("[name='quantity']").type(quantity);
+ }
+
+ clickAddInventory() {
+ cy.intercept("POST", "**/api/v1/facility/*/inventory/").as(
+ "createInventory"
+ );
+ cy.get("button").contains("Add/Update Inventory").click();
+ cy.wait("@createInventory").its("response.statusCode").should("eq", 201);
+ }
+
+ fillResourceRequestDetails(
+ name: string,
+ phone_number: string,
+ facility: string,
+ title: string,
+ quantity: string,
+ description: string
+ ) {
+ cy.get("#refering_facility_contact_name").type(name);
+ cy.get("#refering_facility_contact_number").type(phone_number);
+ cy.get("[name='approving_facility']")
+ .type(facility)
+ .then(() => {
+ cy.get("[role='option']").first().click();
+ });
+ cy.get("#title").type(title);
+ cy.get("#requested_quantity").type(quantity);
+ cy.get("#reason").type(description);
+ }
+
+ clickSubmitRequestButton() {
+ cy.intercept("POST", "**/api/v1/resource/").as("createResourceRequest");
+ cy.get("button").contains("Submit").click();
+ cy.wait("@createResourceRequest")
+ .its("response.statusCode")
+ .should("eq", 201);
+ }
}
export default FacilityPage;
diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts
index e75524ad3f4..f691d5f9e15 100644
--- a/cypress/pageobject/Login/LoginPage.ts
+++ b/cypress/pageobject/Login/LoginPage.ts
@@ -1,4 +1,6 @@
// LoginPage.ts
+import { cy } from "local-cypress";
+
class LoginPage {
loginAsDisctrictAdmin(): void {
cy.loginByApi("devdistrictadmin", "Coronasafe@123");
diff --git a/cypress/pageobject/Resource/ResourcePage.ts b/cypress/pageobject/Resource/ResourcePage.ts
new file mode 100644
index 00000000000..1fecf86b9e1
--- /dev/null
+++ b/cypress/pageobject/Resource/ResourcePage.ts
@@ -0,0 +1,83 @@
+// ResoucrePage.ts
+class ResourcePage {
+ verifyDownloadButtonWorks() {
+ cy.get("svg.care-svg-icon__baseline.care-l-export").each(($button) => {
+ cy.intercept(/\/api\/v1\/resource/).as("resource_download");
+ cy.wrap($button).click({ force: true });
+ cy.wait("@resource_download").then((interception) => {
+ expect(interception.response.statusCode).to.equal(200);
+ });
+ });
+ }
+
+ spyResourceApi() {
+ cy.intercept(/\/api\/v1\/resource/).as("resource");
+ }
+
+ clickCompletedResources() {
+ cy.contains("Completed").click();
+ }
+
+ verifyCompletedResources() {
+ cy.wait("@resource").then((interception) => {
+ expect(interception.response.statusCode).to.equal(200);
+ });
+ cy.contains("Active").should("have.class", "text-primary-500");
+ cy.contains("Completed").should("have.class", "text-white");
+ }
+
+ clickActiveResources() {
+ cy.contains("Active").click();
+ }
+
+ verifyActiveResources() {
+ cy.wait("@resource").then((interception) => {
+ expect(interception.response.statusCode).to.equal(200);
+ });
+ cy.contains("Active").should("have.class", "text-white");
+ cy.contains("Completed").should("have.class", "text-primary-500");
+ }
+
+ clickListViewButton() {
+ cy.contains("List View").click();
+ }
+
+ clickBoardViewButton() {
+ cy.contains("Board View").click();
+ }
+
+ openAlreadyCreatedResource() {
+ cy.get("[data-testid='resource-details']").first().click();
+ }
+
+ clickUpdateStatus() {
+ cy.get("[data-testid='update-status']").click();
+ }
+
+ updateStatus(status: string) {
+ cy.get("#status").click();
+ cy.get("[role='option']").contains(status).click();
+ }
+
+ clickSubmitButton() {
+ cy.intercept("PUT", "**/api/v1/resource/**/").as("updateResource");
+ cy.get("#submit").contains("Submit").click();
+ cy.wait("@updateResource").its("response.statusCode").should("eq", 200);
+ }
+
+ verifySuccessNotification(message: string) {
+ cy.verifyNotification(message);
+ }
+
+ addCommentForResource(comment: string) {
+ cy.get("#comment").type(comment);
+ }
+
+ clickPostCommentButton() {
+ cy.intercept("POST", "**/api/v1/resource/*/comment/").as("postComment");
+ cy.contains("Post Your Comment").click();
+ cy.wait("@postComment").its("response.statusCode").should("eq", 201);
+ }
+}
+
+export default ResourcePage;
diff --git a/src/Components/Common/DateInputV2.tsx b/src/Components/Common/DateInputV2.tsx
index ecee0b8a969..bcebd4e0055 100644
--- a/src/Components/Common/DateInputV2.tsx
+++ b/src/Components/Common/DateInputV2.tsx
@@ -284,7 +284,7 @@ const DateInputV2: React.FC = ({
datePickerHeaderDate.getMonth() - 1
)
}
- className="aspect-square inline-flex cursor-pointer items-center justify-center rounded p-2 transition duration-100 ease-in-out hover:bg-gray-300"
+ className="inline-flex aspect-square cursor-pointer items-center justify-center rounded p-2 transition duration-100 ease-in-out hover:bg-gray-300"
onClick={decrement}
>
@@ -317,7 +317,7 @@ const DateInputV2: React.FC = ({
new Date().getFullYear() === year.getFullYear()) ||
!isDateWithinConstraints(getLastDay())
}
- className="aspect-square inline-flex cursor-pointer items-center justify-center rounded p-2 transition duration-100 ease-in-out hover:bg-gray-300"
+ className="inline-flex aspect-square cursor-pointer items-center justify-center rounded p-2 transition duration-100 ease-in-out hover:bg-gray-300"
onClick={increment}
>
diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx
index ec157d61884..81e7fa4906f 100644
--- a/src/Components/Facility/FacilityHome.tsx
+++ b/src/Components/Facility/FacilityHome.tsx
@@ -565,6 +565,7 @@ export const FacilityHome = (props: any) => {
Location Management
navigate(`/facility/${facilityId}/resource/new`)
}
@@ -593,6 +594,7 @@ export const FacilityHome = (props: any) => {
View Users
setOpenDeleteDialog(true)}
className="flex items-center gap-3"
diff --git a/src/Components/Resource/ListView.tsx b/src/Components/Resource/ListView.tsx
index ab02d31bedd..b368c9bf3ea 100644
--- a/src/Components/Resource/ListView.tsx
+++ b/src/Components/Resource/ListView.tsx
@@ -174,6 +174,7 @@ export default function ListView() {
navigate(`/resource/${resource.external_id}`)}
className="btn btn-default mr-2 w-full bg-white"
>
diff --git a/src/Components/Resource/ResourceBoard.tsx b/src/Components/Resource/ResourceBoard.tsx
index 64a8ef9da35..66bf559be0d 100644
--- a/src/Components/Resource/ResourceBoard.tsx
+++ b/src/Components/Resource/ResourceBoard.tsx
@@ -139,6 +139,7 @@ const ResourceCard = ({ resource }: any) => {
navigate(`/resource/${resource.external_id}`)}
className="btn btn-default mr-2 w-full bg-white"
>
diff --git a/src/Components/Resource/ResourceDetails.tsx b/src/Components/Resource/ResourceDetails.tsx
index 5a98256a605..591f6b48bdb 100644
--- a/src/Components/Resource/ResourceDetails.tsx
+++ b/src/Components/Resource/ResourceDetails.tsx
@@ -271,6 +271,7 @@ export default function ResourceDetails(props: { id: string }) {
{data.title || "--"}
diff --git a/src/index.tsx b/src/index.tsx
index 1cea1c326d9..0c60d9ba2db 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -9,7 +9,7 @@ import * as Sentry from "@sentry/browser";
import "./style/index.css";
import { registerSW } from "virtual:pwa-register";
-if("serviceWorker" in navigator){
+if ("serviceWorker" in navigator) {
registerSW({ immediate: false });
}
From 0c2be8b8c62fb32437d3abf72ceb29d290463a76 Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Thu, 7 Sep 2023 17:18:27 +0000
Subject: [PATCH 16/50] User (Doctor) Autocomplete for Consultation Verified By
(#6194)
* Consultation: Verified By Doctor as Dropdown
* filter by home_facility instead
* fixes #6213
* fix cypress
* fix cypress
* fix more cypressss
* fix moreeeee
* fix inventory
* fix inventory
---
cypress/e2e/patient_spec/patient_crud.cy.ts | 3 +-
.../pageobject/Facility/FacilityCreation.ts | 3 +-
.../pageobject/Patient/PatientConsultation.ts | 9 ++++-
.../Common/UserAutocompleteFormField.tsx | 13 ++++--
.../Facility/ConsultationDetails.tsx | 7 +++-
src/Components/Facility/ConsultationForm.tsx | 40 ++++++++++++-------
src/Components/Facility/models.tsx | 3 ++
src/Components/Users/models.tsx | 17 ++++----
8 files changed, 61 insertions(+), 34 deletions(-)
diff --git a/cypress/e2e/patient_spec/patient_crud.cy.ts b/cypress/e2e/patient_spec/patient_crud.cy.ts
index af639783a80..b55732fa440 100644
--- a/cypress/e2e/patient_spec/patient_crud.cy.ts
+++ b/cypress/e2e/patient_spec/patient_crud.cy.ts
@@ -118,9 +118,10 @@ describe("Patient Creation with consultation", () => {
"170",
"IP007",
"generalnote",
- "generalnote"
+ "Dev Doctor"
);
patientConsultationPage.submitConsultation();
+
// Below code for the prescription module only present while creating a new consultation
patientConsultationPage.clickAddPrescription();
patientConsultationPage.interceptMediaBase();
diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts
index 84b793b3949..3eac29f14c5 100644
--- a/cypress/pageobject/Facility/FacilityCreation.ts
+++ b/cypress/pageobject/Facility/FacilityCreation.ts
@@ -104,6 +104,7 @@ class FacilityPage {
}
clickManageFacilityDropdown() {
+ cy.get("#manage-facility-dropdown button").scrollIntoView();
cy.get("#manage-facility-dropdown button")
.contains("Manage Facility")
.click();
@@ -118,7 +119,7 @@ class FacilityPage {
}
clickInventoryManagementOption() {
- cy.get("[id=inventory-management]").click();
+ cy.get("#inventory-management").click();
}
clickResourceRequestOption() {
diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts
index 315ef455aaa..c0ba0c260d8 100644
--- a/cypress/pageobject/Patient/PatientConsultation.ts
+++ b/cypress/pageobject/Patient/PatientConsultation.ts
@@ -16,7 +16,7 @@ export class PatientConsultationPage {
}
fillIllnessHistory(history: string) {
- cy.get("#history_of_present_illness").scrollIntoView;
+ cy.get("#history_of_present_illness").scrollIntoView();
cy.get("#history_of_present_illness").should("be.visible");
cy.get("#history_of_present_illness").click().type(history);
}
@@ -52,7 +52,12 @@ export class PatientConsultationPage {
.click();
cy.get("#consultation_notes").click().type(consulationNotes);
- cy.get("#verified_by").click().type(verificationBy);
+ cy.get("#verified_by")
+ .click()
+ .type(verificationBy)
+ .then(() => {
+ cy.get("[role='option']").contains("Dev Doctor").click();
+ });
}
submitConsultation() {
diff --git a/src/Components/Common/UserAutocompleteFormField.tsx b/src/Components/Common/UserAutocompleteFormField.tsx
index 6262db47bc3..ebe6d96bbc5 100644
--- a/src/Components/Common/UserAutocompleteFormField.tsx
+++ b/src/Components/Common/UserAutocompleteFormField.tsx
@@ -8,11 +8,13 @@ import {
} from "../Form/FormFields/Utils";
import { UserModel } from "../Users/models";
import { isUserOnline } from "../../Utils/utils";
+import { UserRole } from "../../Common/constants";
type Props = FormFieldBaseProps & {
placeholder?: string;
facilityId?: string;
- userType?: string;
+ homeFacility?: string;
+ userType?: UserRole;
showActiveStatus?: boolean;
};
@@ -26,6 +28,7 @@ export default function UserAutocompleteFormField(props: Props) {
let search_filter: {
limit: number;
offset: number;
+ home_facility?: string;
user_type?: string;
search_text?: string;
} = { limit: 5, offset: 0 };
@@ -34,6 +37,10 @@ export default function UserAutocompleteFormField(props: Props) {
search_filter = { ...search_filter, user_type: props.userType };
}
+ if (props.homeFacility) {
+ search_filter = { ...search_filter, home_facility: props.homeFacility };
+ }
+
const getStatusIcon = (option: UserModel) => {
if (!props.showActiveStatus) return null;
@@ -69,11 +76,11 @@ export default function UserAutocompleteFormField(props: Props) {
onQuery={(query) =>
fetchOptions(
props.facilityId
- ? getFacilityUsers(props.facilityId)
- : getUserList({
+ ? getFacilityUsers(props.facilityId, {
...search_filter,
search_text: query,
})
+ : getUserList({ ...search_filter, search_text: query })
)
}
isLoading={isLoading}
diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx
index 194d6629fc8..401f113a812 100644
--- a/src/Components/Facility/ConsultationDetails.tsx
+++ b/src/Components/Facility/ConsultationDetails.tsx
@@ -469,12 +469,15 @@ export const ConsultationDetails = (props: any) => {
label="Diagnosis (as per ICD-11 recommended by WHO)"
/>
- {consultationData.verified_by && (
+ {(consultationData.verified_by_object ||
+ consultationData.deprecated_verified_by) && (
Verified By:{" "}
- {consultationData.verified_by}
+ {consultationData.verified_by_object
+ ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
+ : consultationData.deprecated_verified_by}
)}
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx
index 435529d48a9..fbea6dd8a5a 100644
--- a/src/Components/Facility/ConsultationForm.tsx
+++ b/src/Components/Facility/ConsultationForm.tsx
@@ -40,7 +40,10 @@ import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField";
import DateFormField from "../Form/FormFields/DateFormField";
import { DiagnosisSelectFormField } from "../Common/DiagnosisSelectFormField";
import { FacilitySelect } from "../Common/FacilitySelect";
-import { FieldChangeEventHandler } from "../Form/FormFields/Utils";
+import {
+ FieldChangeEvent,
+ FieldChangeEventHandler,
+} from "../Form/FormFields/Utils";
import { FormAction } from "../Form/Utils";
import PatientCategorySelect from "../Patient/PatientCategorySelect";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
@@ -83,6 +86,7 @@ type FormDetails = {
icd11_provisional_diagnoses_object: ICD11DiagnosisModel[];
icd11_principal_diagnosis?: ICD11DiagnosisModel["id"];
verified_by: string;
+ verified_by_object: UserModel | null;
is_kasp: BooleanStrings;
kasp_enabled_date: null;
examination_details: string;
@@ -128,6 +132,7 @@ const initForm: FormDetails = {
icd11_provisional_diagnoses_object: [],
icd11_principal_diagnosis: undefined,
verified_by: "",
+ verified_by_object: null,
is_kasp: "false",
kasp_enabled_date: null,
examination_details: "",
@@ -329,12 +334,13 @@ export const ConsultationForm = (props: any) => {
?.id ?? "Comfort"
: "Comfort",
patient_no: res.data.patient_no ?? "",
- verified_by: res.data.verified_by ? res.data.verified_by : "",
OPconsultation: res.data.consultation_notes,
is_telemedicine: `${res.data.is_telemedicine}`,
is_kasp: `${res.data.is_kasp}`,
assigned_to: res.data.assigned_to || "",
assigned_to_object: res.data.assigned_to_object,
+ verified_by: res.data.verified_by || "",
+ verified_by_object: res.data.verified_by_object,
ett_tt: res.data.ett_tt ? Number(res.data.ett_tt) : 3,
special_instruction: res.data.special_instruction || "",
weight: res.data.weight ? res.data.weight : "",
@@ -745,7 +751,6 @@ export const ConsultationForm = (props: any) => {
symptoms_onset_date: new Date(),
category: "Critical",
suggestion: "DD",
- verified_by: "Brought Dead",
},
});
} else if (event.name === "suggestion" && event.value === "DD") {
@@ -778,14 +783,14 @@ export const ConsultationForm = (props: any) => {
}
};
- const handleDoctorSelect = (doctor: UserModel | null) => {
- if (doctor?.id) {
+ const handleDoctorSelect = (event: FieldChangeEvent) => {
+ if (event.value?.id) {
dispatch({
type: "set_form",
form: {
...state.form,
- assigned_to: doctor.id.toString(),
- assigned_to_object: doctor,
+ [event.name]: event.value.id.toString(),
+ [`${event.name}_object`]: event.value,
},
});
} else {
@@ -793,8 +798,8 @@ export const ConsultationForm = (props: any) => {
type: "set_form",
form: {
...state.form,
- assigned_to: "",
- assigned_to_object: null,
+ [event.name]: "",
+ [`${event.name}_object`]: null,
},
});
}
@@ -1309,11 +1314,17 @@ export const ConsultationForm = (props: any) => {
className="col-span-6"
ref={fieldRef["verified_by"]}
>
-
@@ -1358,11 +1369,10 @@ export const ConsultationForm = (props: any) => {
value={
state.form.assigned_to_object ?? undefined
}
- onChange={(option) =>
- handleDoctorSelect(option.value)
- }
+ onChange={handleDoctorSelect}
userType={"Doctor"}
name={"assigned_to"}
+ label="Assigned to"
/>
)}
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx
index 9666f20cfb8..012ca1d68b2 100644
--- a/src/Components/Facility/models.tsx
+++ b/src/Components/Facility/models.tsx
@@ -2,6 +2,7 @@ import { AssignedToObjectModel } from "../Patient/models";
import { ProcedureType } from "../Common/prescription-builder/ProcedureBuilder";
import { NormalPrescription, PRNPrescription } from "../Medicine/models";
import { AssetData } from "../Assets/AssetTypes";
+import { UserBareMinimum } from "../Users/models";
export interface LocalBodyModel {
name: string;
@@ -113,7 +114,9 @@ export interface ConsultationModel {
icd11_diagnoses_object?: ICD11DiagnosisModel[];
icd11_provisional_diagnoses_object?: ICD11DiagnosisModel[];
icd11_principal_diagnosis?: ICD11DiagnosisModel["id"];
+ deprecated_verified_by?: string;
verified_by?: string;
+ verified_by_object?: UserBareMinimum;
suggestion_text?: string;
symptoms?: Array;
symptoms_text?: string;
diff --git a/src/Components/Users/models.tsx b/src/Components/Users/models.tsx
index a59802ab440..ae3229933c5 100644
--- a/src/Components/Users/models.tsx
+++ b/src/Components/Users/models.tsx
@@ -5,13 +5,18 @@ interface HomeFacilityObjectModel {
id?: string;
name?: string;
}
-export type UserModel = {
+
+export type UserBareMinimum = {
id: number;
username: string;
first_name: string;
last_name: string;
email: string;
user_type: UserRole;
+ last_login: string | undefined;
+};
+
+export type UserModel = UserBareMinimum & {
local_body?: number;
district?: number;
state?: number;
@@ -21,7 +26,6 @@ export type UserModel = {
age?: number;
is_superuser?: boolean;
verified?: boolean;
- last_login: string | undefined;
home_facility_object?: HomeFacilityObjectModel;
local_body_object?: LocalBodyModel;
district_object?: DistrictModel;
@@ -42,13 +46,7 @@ export interface SkillModel {
skill_object: SkillObjectModel;
}
-export interface UserAssignedModel {
- id?: number;
- username?: string;
- first_name?: string;
- last_name?: string;
- email?: string;
- user_type?: number | string;
+export interface UserAssignedModel extends UserBareMinimum {
local_body?: number;
district?: number;
state?: number;
@@ -58,7 +56,6 @@ export interface UserAssignedModel {
age?: number;
is_superuser?: boolean;
verified?: boolean;
- last_login?: Date;
home_facility_object?: HomeFacilityObjectModel;
doctor_qualification?: string;
doctor_experience_commenced_on?: Date;
From 4519b08aca6d4e2e6df774789b13d8a3d9d258f4 Mon Sep 17 00:00:00 2001
From: Khavin Shankar
Date: Fri, 8 Sep 2023 07:28:49 +0530
Subject: [PATCH 17/50] Temporarily expose abdm health facility config directly
(#6197)
---
.../ABDM/ConfigureHealthFacility.tsx | 81 ++++++++++++++++++-
.../Facility/UpdateFacilityMiddleware.tsx | 9 +--
src/Redux/actions.tsx | 9 +++
src/Redux/api.tsx | 5 ++
4 files changed, 95 insertions(+), 9 deletions(-)
diff --git a/src/Components/ABDM/ConfigureHealthFacility.tsx b/src/Components/ABDM/ConfigureHealthFacility.tsx
index c0a74ccf106..025a3f0e862 100644
--- a/src/Components/ABDM/ConfigureHealthFacility.tsx
+++ b/src/Components/ABDM/ConfigureHealthFacility.tsx
@@ -6,6 +6,7 @@ import * as Notification from "../../Utils/Notifications.js";
import { navigate } from "raviger";
import { Cancel, Submit } from "../Common/components/ButtonV2";
import TextFormField from "../Form/FormFields/TextFormField";
+import { classNames } from "../../Utils/utils";
const Loading = lazy(() => import("../Common/Loading"));
const initForm = {
@@ -47,7 +48,7 @@ export const ConfigureHealthFacility = (props: any) => {
setIsLoading(true);
const res = await dispatchAction(healthFacilityActions.read(facilityId));
- if (res.status === 200 && res?.data) {
+ if (res?.status === 200 && res?.data) {
const formData = {
...state.form,
hf_id: res.data.hf_id,
@@ -84,6 +85,32 @@ export const ConfigureHealthFacility = (props: any) => {
hf_id: state.form.hf_id,
})
);
+ } else if (state.form.hf_id === state.form.health_facility?.hf_id) {
+ res = await dispatchAction(
+ healthFacilityActions.registerService(facilityId)
+ );
+
+ if (res?.status === 200 && res?.data) {
+ if (res.data?.registered) {
+ dispatch({
+ type: "set_form",
+ form: {
+ ...state.form,
+ health_facility: {
+ ...state.form?.health_facility,
+ registered: res.data.registered,
+ },
+ },
+ });
+
+ return;
+ }
+ }
+
+ Notification.Error({
+ msg: "Service registration failed, please try again later",
+ });
+ return;
} else {
res = await dispatchAction(
healthFacilityActions.create({
@@ -127,6 +154,49 @@ export const ConfigureHealthFacility = (props: any) => {
+ {state.form.health_facility?.registered ? (
+ <>
+
+
+ The ABDM health facility is successfully linked with
+ care{" "}
+ and registered as a service in bridge
+
+
+ No Action Required.
+
+
+ Registered
+ >
+ ) : (
+ <>
+
+
+ The ABDM health facility is successfully linked with
+ care{" "}
+
+ but not registered as a service in bridge
+
+
+
+ Click on Link Health Facility to
+ register the service
+
+
+ Not Registered
+ >
+ )}
+
+ }
required
value={state.form.hf_id}
onChange={(e) => handleChange(e)}
@@ -136,7 +206,14 @@ export const ConfigureHealthFacility = (props: any) => {
navigate(`/facility/${facilityId}`)} />
-
+
diff --git a/src/Components/Facility/UpdateFacilityMiddleware.tsx b/src/Components/Facility/UpdateFacilityMiddleware.tsx
index 4fd5a4f24b0..211d8cf458a 100644
--- a/src/Components/Facility/UpdateFacilityMiddleware.tsx
+++ b/src/Components/Facility/UpdateFacilityMiddleware.tsx
@@ -11,7 +11,6 @@ import { navigate } from "raviger";
import { Cancel, Submit } from "../Common/components/ButtonV2";
import TextFormField from "../Form/FormFields/TextFormField";
import Page from "../Common/components/Page";
-import useConfig from "../../Common/hooks/useConfig";
import { ConfigureHealthFacility } from "../ABDM/ConfigureHealthFacility";
const Loading = lazy(() => import("../Common/Loading"));
@@ -52,7 +51,6 @@ export const UpdateFacilityMiddleware = (props: any) => {
const { facilityId } = props;
const dispatchAction: any = useDispatch();
const [isLoading, setIsLoading] = useState(false);
- const config = useConfig();
const fetchData = useCallback(
async (status: statusType) => {
@@ -169,11 +167,8 @@ export const UpdateFacilityMiddleware = (props: any) => {
- {config.enable_abdm ? (
-
- ) : (
- <>>
- )}
+
+
);
};
diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx
index e3212685d2f..6e0d91fc59d 100644
--- a/src/Redux/actions.tsx
+++ b/src/Redux/actions.tsx
@@ -979,6 +979,15 @@ export const healthFacilityActions = {
facility_id: id,
});
},
+
+ registerService: (id: string) => {
+ return fireRequest(
+ "registerHealthFacilityAsService",
+ [],
+ {},
+ { facility_id: id }
+ );
+ },
};
export const listAssetAvailability = (params: object) =>
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 3efff35689e..393b9372ca5 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -931,6 +931,11 @@ const routes: Routes = {
method: "PATCH",
},
+ registerHealthFacilityAsService: {
+ path: "/api/v1/abdm/health_facility/{facility_id}/register_service/",
+ method: "POST",
+ },
+
// Asset Availability endpoints
listAssetAvailability: {
From 8bcf91dce41cb8c20d7b2b674550dcb73a0ca746 Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Fri, 8 Sep 2023 06:22:14 +0000
Subject: [PATCH 18/50] cypress: fix inventory management (#6245)
* fix cypress :/
* Update cypress/pageobject/Facility/FacilityCreation.ts
* fix double click
---------
Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com>
---
cypress/pageobject/Facility/FacilityCreation.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts
index 3eac29f14c5..aa46d4f1105 100644
--- a/cypress/pageobject/Facility/FacilityCreation.ts
+++ b/cypress/pageobject/Facility/FacilityCreation.ts
@@ -119,6 +119,7 @@ class FacilityPage {
}
clickInventoryManagementOption() {
+ cy.get("#inventory-management", { timeout: 10000 }).should("be.visible");
cy.get("#inventory-management").click();
}
@@ -162,8 +163,6 @@ class FacilityPage {
cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities");
cy.get("[id='facility-details']").first().click();
cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
- cy.get("#manage-facility-dropdown button").should("be.visible");
- cy.get("[id=manage-facility-dropdown]").scrollIntoView().click();
}
clickManageInventory() {
From 9b45c5e9a496c54111e07232f1015c68ebb6205b Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Fri, 8 Sep 2023 19:24:23 +0530
Subject: [PATCH 19/50] Fix verified_by and Principal Diagnosis validation
(#6247)
* Fix verified_by and Principal Diagnosis validation
* Apply suggestions from code review
Co-authored-by: Rithvik Nishad
---
src/Components/Facility/ConsultationForm.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx
index fbea6dd8a5a..97f35f292f8 100644
--- a/src/Components/Facility/ConsultationForm.tsx
+++ b/src/Components/Facility/ConsultationForm.tsx
@@ -538,7 +538,7 @@ export const ConsultationForm = (props: any) => {
}
case "verified_by": {
- if (!state.form[field].replace(/\s/g, "").length) {
+ if (!state.form[field]) {
errors[field] = "Please fill verified by";
invalidForm = true;
break;
@@ -570,6 +570,7 @@ export const ConsultationForm = (props: any) => {
if (
state.form[field] &&
state.form["icd11_diagnoses_object"].length &&
+ !state.form["icd11_provisional_diagnoses_object"] &&
!state.form["icd11_diagnoses_object"]
.map((d) => d.id)
.includes(state.form[field]!)
@@ -583,6 +584,7 @@ export const ConsultationForm = (props: any) => {
if (
state.form[field] &&
state.form["icd11_provisional_diagnoses_object"].length &&
+ !state.form["icd11_diagnoses_object"] &&
!state.form["icd11_provisional_diagnoses_object"]
.map((d) => d.id)
.includes(state.form[field]!)
From fb4e7bed464b858e5a127b9701c5caa1660a8447 Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Fri, 8 Sep 2023 15:28:47 +0000
Subject: [PATCH 20/50] Consultation Form: skip validation for `verified_by` if
`suggestion` is Declare Death and fix discharge not working on create
consultation (#6248)
* fix verified by validation
* fix decalre dead
---
src/Components/Facility/ConsultationForm.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx
index 97f35f292f8..49028a62953 100644
--- a/src/Components/Facility/ConsultationForm.tsx
+++ b/src/Components/Facility/ConsultationForm.tsx
@@ -538,7 +538,7 @@ export const ConsultationForm = (props: any) => {
}
case "verified_by": {
- if (!state.form[field]) {
+ if (state.form.suggestion !== "DD" && !state.form[field]) {
errors[field] = "Please fill verified by";
invalidForm = true;
break;
@@ -618,6 +618,7 @@ export const ConsultationForm = (props: any) => {
};
const declareThePatientDead = async (
+ id: string,
cause_of_death: string,
death_datetime: string,
death_confirmed_doctor: string
@@ -629,6 +630,7 @@ export const ConsultationForm = (props: any) => {
discharge_notes: cause_of_death,
death_datetime: death_datetime,
death_confirmed_doctor: death_confirmed_doctor,
+ discharge_date: dayjs().toISOString(),
},
{ id }
)
@@ -714,6 +716,7 @@ export const ConsultationForm = (props: any) => {
if (data.suggestion === "DD") {
await declareThePatientDead(
+ res.data.id,
state.form.cause_of_death,
state.form.death_datetime,
state.form.death_confirmed_doctor
From ca51ef15bad191bb89c59c1b8641320e817606f4 Mon Sep 17 00:00:00 2001
From: "Tasnimul H. Tauhid"
Date: Mon, 11 Sep 2023 13:57:31 +0530
Subject: [PATCH 21/50] Refactor Consultation Details (#6258)
---
.../Facility/ConsultationDetails.tsx | 1324 -----------------
.../ConsultationABGTab.tsx | 22 +
.../ConsultationDialysisTab.tsx | 14 +
.../ConsultationFeedTab.tsx | 22 +
.../ConsultationFilesTab.tsx | 18 +
.../ConsultationInvestigationsTab.tsx | 39 +
.../ConsultationMedicinesTab.tsx | 19 +
.../ConsultationNeurologicalMonitoringTab.tsx | 24 +
.../ConsultationNeutritionTab.tsx | 18 +
.../ConsultationNursingTab.tsx | 18 +
.../ConsultationPressureSoreTab.tsx | 14 +
.../ConsultationSummaryTab.tsx | 22 +
.../ConsultationUpdatesTab.tsx | 690 +++++++++
.../ConsultationVentilatorTab.tsx | 22 +
.../Facility/ConsultationDetails/index.tsx | 501 +++++++
15 files changed, 1443 insertions(+), 1324 deletions(-)
delete mode 100644 src/Components/Facility/ConsultationDetails.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
create mode 100644 src/Components/Facility/ConsultationDetails/index.tsx
diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx
deleted file mode 100644
index 401f113a812..00000000000
--- a/src/Components/Facility/ConsultationDetails.tsx
+++ /dev/null
@@ -1,1324 +0,0 @@
-import { AssetBedModel, AssetClass, AssetData } from "../Assets/AssetTypes";
-import {
- CONSULTATION_TABS,
- DISCHARGE_REASONS,
- GENDER_TYPES,
- OptionsType,
- SYMPTOM_CHOICES,
-} from "../../Common/constants";
-import {
- BedModel,
- ConsultationModel,
- FacilityModel,
- ICD11DiagnosisModel,
-} from "./models";
-import {
- getConsultation,
- getPatient,
- getPermittedFacility,
- listAssetBeds,
-} from "../../Redux/actions";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-import { lazy, useCallback, useEffect, useState } from "react";
-
-import { ABGPlots } from "./Consultations/ABGPlots";
-import ButtonV2 from "../Common/components/ButtonV2";
-import CareIcon from "../../CAREUI/icons/CareIcon";
-import Chip from "../../CAREUI/display/Chip";
-import { DailyRoundsList } from "./Consultations/DailyRoundsList";
-import { DialysisPlots } from "./Consultations/DialysisPlots";
-import DischargeModal from "./DischargeModal";
-import DischargeSummaryModal from "./DischargeSummaryModal";
-import DoctorVideoSlideover from "./DoctorVideoSlideover";
-import { Feed } from "./Consultations/Feed";
-import { FileUpload } from "../Patient/FileUpload";
-import HL7PatientVitalsMonitor from "../VitalsMonitor/HL7PatientVitalsMonitor";
-import InvestigationTab from "./Investigations/investigationsTab";
-import { make as Link } from "../Common/components/Link.bs";
-import { NeurologicalTable } from "./Consultations/NeurologicalTables";
-import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
-import { NursingPlot } from "./Consultations/NursingPlot";
-import { NutritionPlots } from "./Consultations/NutritionPlots";
-import PatientInfoCard from "../Patient/PatientInfoCard";
-import { PatientModel } from "../Patient/models";
-import PrescriptionsTable from "../Medicine/PrescriptionsTable";
-import { PressureSoreDiagrams } from "./Consultations/PressureSoreDiagrams";
-import { PrimaryParametersPlot } from "./Consultations/PrimaryParametersPlot";
-import ReadMore from "../Common/components/Readmore";
-import VentilatorPatientVitalsMonitor from "../VitalsMonitor/VentilatorPatientVitalsMonitor";
-import { VentilatorPlot } from "./Consultations/VentilatorPlot";
-import { formatDate, formatDateTime, relativeTime } from "../../Utils/utils";
-
-import { navigate } from "raviger";
-import { useDispatch } from "react-redux";
-import { useQueryParams } from "raviger";
-import { useTranslation } from "react-i18next";
-import { triggerGoal } from "../Common/Plausible";
-import useVitalsAspectRatioConfig from "../VitalsMonitor/useVitalsAspectRatioConfig";
-import useAuthUser from "../../Common/hooks/useAuthUser";
-import PrescriptionAdministrationsTable from "../Medicine/PrescriptionAdministrationsTable";
-
-const Loading = lazy(() => import("../Common/Loading"));
-const PageTitle = lazy(() => import("../Common/PageTitle"));
-const symptomChoices = [...SYMPTOM_CHOICES];
-
-export const ConsultationDetails = (props: any) => {
- const { t } = useTranslation();
- const { facilityId, patientId, consultationId } = props;
- const tab = props.tab.toUpperCase();
- const dispatch: any = useDispatch();
- const [isLoading, setIsLoading] = useState(false);
- const [showDoctors, setShowDoctors] = useState(false);
- const [qParams, _] = useQueryParams();
-
- const [consultationData, setConsultationData] = useState(
- {} as ConsultationModel
- );
- const [patientData, setPatientData] = useState({});
- const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
- useState(false);
- const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
- const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
-
- const getPatientGender = (patientData: any) =>
- GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
-
- const getPatientAddress = (patientData: any) =>
- `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
-
- const getPatientComorbidities = (patientData: any) => {
- if (patientData?.medical_history?.length) {
- const medHis = patientData.medical_history;
- return medHis.map((item: any) => item.disease).join(", ");
- } else {
- return "None";
- }
- };
-
- const [hl7SocketUrl, setHL7SocketUrl] = useState();
- const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
- const [monitorBedData, setMonitorBedData] = useState();
- const [ventilatorBedData, setVentilatorBedData] = useState();
- const authUser = useAuthUser();
-
- useEffect(() => {
- if (
- !consultationData.facility ||
- !consultationData.current_bed?.bed_object.id
- )
- return;
-
- const fetchData = async () => {
- const [facilityRes, assetBedRes] = await Promise.all([
- dispatch(getPermittedFacility(consultationData.facility as any)),
- dispatch(
- listAssetBeds({
- facility: consultationData.facility as any,
- bed: consultationData.current_bed?.bed_object.id,
- })
- ),
- ]);
-
- const { middleware_address } = facilityRes.data as FacilityModel;
- const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
-
- const monitorBedData = assetBeds?.find(
- (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
- );
- setMonitorBedData(monitorBedData);
- const assetDataForMonitor = monitorBedData?.asset_object;
- const hl7Meta = assetDataForMonitor?.meta;
- const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
- if (hl7Middleware && hl7Meta?.local_ip_address) {
- setHL7SocketUrl(
- `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
- );
- }
-
- const consultationBedVentilator =
- consultationData?.current_bed?.assets_objects?.find(
- (i) => i.asset_class === AssetClass.VENTILATOR
- );
- let ventilatorBedData;
- if (consultationBedVentilator) {
- ventilatorBedData = {
- asset_object: consultationBedVentilator,
- bed_object: consultationData?.current_bed?.bed_object,
- } as AssetBedModel;
- } else {
- ventilatorBedData = assetBeds?.find(
- (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
- );
- }
- setVentilatorBedData(ventilatorBedData);
- const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
- const ventilatorMiddleware =
- ventilatorMeta?.middleware_hostname || middleware_address;
- if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
- setVentilatorSocketUrl(
- `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
- );
- }
-
- if (
- !(hl7Middleware && hl7Meta?.local_ip_address) &&
- !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
- ) {
- setHL7SocketUrl(undefined);
- setVentilatorSocketUrl(undefined);
- }
- };
-
- fetchData();
- }, [consultationData]);
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const res = await dispatch(getConsultation(consultationId));
- if (!status.aborted) {
- if (res?.data) {
- const data: ConsultationModel = {
- ...res.data,
- symptoms_text: "",
- };
- if (res.data.symptoms?.length) {
- const symptoms = res.data.symptoms
- .filter((symptom: number) => symptom !== 9)
- .map((symptom: number) => {
- const option = symptomChoices.find((i) => i.id === symptom);
- return option ? option.text.toLowerCase() : symptom;
- });
- data.symptoms_text = symptoms.join(", ");
- }
- setConsultationData(data);
- const id = res.data.patient;
- const patientRes = await dispatch(getPatient({ id }));
- if (patientRes?.data) {
- const patientGender = getPatientGender(patientRes.data);
- const patientAddress = getPatientAddress(patientRes.data);
- const patientComorbidities = getPatientComorbidities(
- patientRes.data
- );
- const data = {
- ...patientRes.data,
- gender: patientGender,
- address: patientAddress,
- comorbidities: patientComorbidities,
- is_declared_positive: patientRes.data.is_declared_positive
- ? "Yes"
- : "No",
- is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
- };
- setPatientData(data);
- }
- } else {
- navigate("/not-found");
- }
- setIsLoading(false);
- }
- },
- [consultationId, dispatch, patientData.is_vaccinated]
- );
-
- useAbortableEffect((status: statusType) => {
- fetchData(status);
- triggerGoal("Patient Consultation Viewed", {
- facilityId: facilityId,
- consultationId: consultationId,
- userId: authUser.id,
- });
- }, []);
-
- const vitals = useVitalsAspectRatioConfig({
- default: undefined,
- md: 8 / 11,
- lg: 15 / 11,
- xl: 13 / 11,
- "2xl": 19 / 11,
- "3xl": 23 / 11,
- });
-
- if (isLoading) {
- return ;
- }
-
- const tabButtonClasses = (selected: boolean) =>
- `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
- selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
- }`;
-
- const ShowDiagnosis = ({
- diagnoses = [],
- label = "Diagnosis",
- nshow = 2,
- }: {
- diagnoses: ICD11DiagnosisModel[] | undefined;
- label: string;
- nshow?: number;
- }) => {
- const [showMore, setShowMore] = useState(false);
-
- return diagnoses.length ? (
-
- ) : null;
- };
-
- return (
-
-
setOpenDischargeSummaryDialog(false)}
- />
-
- setOpenDischargeDialog(false)}
- consultationData={consultationData}
- />
-
-
-
-
-
- {!consultationData.discharge_date && (
-
-
- navigate(
- `/facility/${patientData.facility}/patient/${patientData.id}/shift/new`
- )
- }
- className="btn btn-primary m-1 w-full hover:text-white"
- >
-
- Shift Patient
-
- {
- triggerGoal("Doctor Connect Clicked", {
- consultationId,
- facilityId: patientData.facility,
- userId: authUser.id,
- page: "ConsultationDetails",
- });
- setShowDoctors(true);
- }}
- className="btn btn-primary m-1 w-full hover:text-white"
- >
- Doctor Connect
-
- {patientData.last_consultation?.id && (
-
- Camera Feed
-
- )}
-
- )}
-
-
- Patient Details
-
-
- Doctor's Notes
-
-
-
-
-
-
-
-
-
- {consultationData.admitted_to && (
-
-
- Patient
- {consultationData.discharge_date
- ? " Discharged from"
- : " Admitted to"}
-
- {consultationData.admitted_to}
-
-
- {(consultationData.admission_date ??
- consultationData.discharge_date) && (
-
- {relativeTime(
- consultationData.discharge_date
- ? consultationData.discharge_date
- : consultationData.admission_date
- )}
-
- )}
-
- {consultationData.admission_date &&
- formatDateTime(consultationData.admission_date)}
- {consultationData.discharge_date &&
- ` - ${formatDateTime(consultationData.discharge_date)}`}
-
-
- )}
-
-
-
-
- {/*consultationData.other_symptoms && (
-
-
- Other Symptoms:{" "}
-
- {consultationData.other_symptoms}
-
- )*/}
-
- {consultationData.icd11_principal_diagnosis && (
-
- d.id === consultationData.icd11_principal_diagnosis
- )!,
- ]}
- />
- )}
-
-
-
-
-
- {(consultationData.verified_by_object ||
- consultationData.deprecated_verified_by) && (
-
-
- Verified By:{" "}
-
- {consultationData.verified_by_object
- ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
- : consultationData.deprecated_verified_by}
-
-
- )}
-
-
- setOpenDischargeSummaryDialog(true)}>
-
- {t("discharge_summary")}
-
-
- setOpenDischargeDialog(true)}
- disabled={!!consultationData.discharge_date}
- >
-
- {t("discharge_from_care")}
-
-
-
-
-
-
- Created:
- {consultationData.created_date
- ? formatDateTime(consultationData.created_date)
- : "--:--"}{" "}
- |
-
- {consultationData.created_by && (
-
- {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
- {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
-
- )}
-
-
-
- Last Modified:
- {consultationData.modified_date
- ? formatDateTime(consultationData.modified_date)
- : "--:--"}{" "}
- |
-
- {consultationData.last_edited_by && (
-
- {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
- {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
-
- )}
-
-
-
-
-
-
-
-
-
- {CONSULTATION_TABS.map((p: OptionsType) => {
- if (p.text === "FEED") {
- if (
- !consultationData?.current_bed?.bed_object?.id ??
- consultationData?.discharge_date !== null
- )
- return null;
- }
- return (
-
- {p.desc}
-
- );
- })}
-
-
-
-
- {tab === "UPDATES" && (
-
- {!consultationData.discharge_date &&
- hl7SocketUrl &&
- ventilatorSocketUrl && (
-
- )}
-
-
-
-
- {!consultationData.discharge_date &&
- ((hl7SocketUrl && !ventilatorSocketUrl) ||
- (!hl7SocketUrl && ventilatorSocketUrl)) && (
-
- {(hl7SocketUrl || ventilatorSocketUrl) && (
-
- {hl7SocketUrl && (
-
-
-
- )}
- {ventilatorSocketUrl && (
-
-
-
- )}
-
- )}
-
- )}
- {consultationData.discharge_date && (
-
-
-
- Discharge Information
-
-
-
- Reason {" - "}
-
- {DISCHARGE_REASONS.find(
- (d) =>
- d.id === consultationData.discharge_reason
- )?.text ?? "--"}
-
-
- {consultationData.discharge_reason === "REF" && (
-
- Referred Facility {" - "}
-
- {consultationData.referred_to_external ||
- consultationData.referred_to_object?.name ||
- "--"}
-
-
- )}
- {consultationData.discharge_reason === "REC" && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Advice {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
-
-
-
- )}
- {consultationData.discharge_reason === "EXP" && (
-
-
- Date of Death {" - "}
-
- {consultationData.death_datetime
- ? formatDateTime(
- consultationData.death_datetime
- )
- : "--:--"}
-
-
-
- Cause of death {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- Confirmed By {" - "}
-
- {consultationData.death_confirmed_doctor ??
- "--"}
-
-
-
- )}
- {["REF", "LAMA"].includes(
- consultationData.discharge_reason ?? ""
- ) && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Notes {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- )}
-
-
-
- )}
- {consultationData.symptoms_text && (
-
-
-
- Symptoms
-
-
-
- Last Daily Update
-
- {consultationData.last_daily_round
- ?.additional_symptoms && (
- <>
-
- {consultationData.last_daily_round?.additional_symptoms.map(
- (symptom: any, index: number) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.last_daily_round
- ?.other_symptoms && (
-
-
- Other Symptoms:
-
- {
- consultationData.last_daily_round
- ?.other_symptoms
- }
-
- )}
-
- from{" "}
- {formatDate(
- consultationData.last_daily_round.created_at
- )}
-
- >
- )}
-
-
- Consultation Update
-
-
- {consultationData.symptoms?.map(
- (symptom, index) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.other_symptoms && (
-
-
- Other Symptoms:
-
- {consultationData.other_symptoms}
-
- )}
-
- from{" "}
- {consultationData.symptoms_onset_date
- ? formatDate(consultationData.symptoms_onset_date)
- : "--/--/----"}
-
-
-
-
- )}
-
- {consultationData.history_of_present_illness && (
-
-
-
- History of Present Illness
-
-
-
-
-
-
- )}
-
- {consultationData.examination_details && (
-
-
-
- Examination details and Clinical conditions:{" "}
-
-
-
-
-
-
- )}
- {consultationData.treatment_plan && (
-
-
-
- Treatment Summary
-
-
-
-
-
-
- )}
- {consultationData.consultation_notes && (
-
-
-
- General Instructions
-
-
-
-
-
-
- )}
-
- {(consultationData.operation ??
- consultationData.special_instruction) && (
-
-
-
- Notes
-
-
- {consultationData.operation && (
-
-
Operation
-
-
- )}
-
- {consultationData.special_instruction && (
-
-
Special Instruction
-
-
- )}
-
-
-
- )}
-
- {consultationData.procedure &&
- consultationData.procedure.length > 0 && (
-
-
-
-
-
-
- Procedure
-
-
- Notes
-
-
- Repetitive
-
-
- Time / Frequency
-
-
-
-
- {consultationData.procedure?.map(
- (procedure, index) => (
-
-
- {procedure.procedure}
-
-
- {procedure.notes}
-
-
- {procedure.repetitive ? "Yes" : "No"}
-
-
- {procedure.repetitive
- ? procedure.frequency
- : formatDateTime(String(procedure.time))}
-
-
- )
- )}
-
-
-
-
- )}
- {consultationData.intubation_start_date && (
-
-
-
- Date/Size/LL:{" "}
-
-
-
- Intubation Date{" - "}
-
- {formatDateTime(
- consultationData.intubation_start_date
- )}
-
-
-
- Extubation Date{" - "}
-
- {consultationData.intubation_end_date &&
- formatDateTime(
- consultationData.intubation_end_date
- )}
-
-
-
- ETT/TT (mmid){" - "}
-
- {consultationData.ett_tt}
-
-
-
- Cuff Pressure (mmhg){" - "}
-
- {consultationData.cuff_pressure}
-
-
-
-
-
- )}
-
- {consultationData.lines?.length > 0 && (
-
-
-
- Lines and Catheters
-
-
- {consultationData.lines?.map(
- (line: any, idx: number) => (
-
-
{line.type}
-
- Details:
-
- {line.other_type}
-
-
- Insertion Date:{" "}
-
- {formatDateTime(line.start_date)}
-
-
-
- Site/Level of Fixation:
-
- {line.site}
-
-
-
- )
- )}
-
-
-
- )}
-
-
-
-
- Body Details
-
-
-
- Gender {" - "}
-
- {patientData.gender ?? "-"}
-
-
-
- Age {" - "}
-
- {patientData.age ?? "-"}
-
-
-
- Weight {" - "}
-
- {consultationData.weight ?? "-"} Kg
-
-
-
- Height {" - "}
-
- {consultationData.height ?? "-"} cm
-
-
-
- Body Surface Area {" - "}
-
- {Math.sqrt(
- (Number(consultationData.weight) *
- Number(consultationData.height)) /
- 3600
- ).toFixed(2)}{" "}
- m2
-
-
-
- Blood Group {" - "}
-
- {patientData.blood_group ?? "-"}
-
-
-
-
-
-
-
-
-
-
- setShowAutomatedRounds((s) => !s)}
- />
-
- Show Automated Rounds
-
-
-
-
-
-
-
- )}
- {tab === "FEED" && (
-
- )}
- {tab === "SUMMARY" && (
-
- )}
- {tab === "MEDICINES" && (
-
- )}
- {tab === "FILES" && (
-
-
-
- )}
-
- {tab === "ABG" && (
-
- )}
- {tab === "NURSING" && (
-
- )}
- {tab === "NEUROLOGICAL_MONITORING" && (
-
- )}
- {tab === "VENTILATOR" && (
-
- )}
- {tab === "NUTRITION" && (
-
- )}
- {tab === "PRESSURE_SORE" && (
-
- )}
- {tab === "DIALYSIS" && (
-
- )}
- {tab === "INVESTIGATIONS" && (
-
-
-
-
-
- navigate(
- `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/investigation/`
- )
- }
- >
-
- {t("log_lab_results")}
-
-
-
-
-
- )}
-
-
-
-
- );
-};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
new file mode 100644
index 00000000000..cf0ce7bd6af
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { ABGPlots } from "../Consultations/ABGPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationABGTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
new file mode 100644
index 00000000000..c3515a80953
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { DialysisPlots } from "../Consultations/DialysisPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationDialysisTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
new file mode 100644
index 00000000000..f4fbc08331c
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { Feed } from "../Consultations/Feed";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationFeedTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
new file mode 100644
index 00000000000..754d4bf7b86
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
@@ -0,0 +1,18 @@
+import { ConsultationTabProps } from "./index";
+import { FileUpload } from "../../Patient/FileUpload";
+
+export const ConsultationFilesTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
new file mode 100644
index 00000000000..69b11a30ed5
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
@@ -0,0 +1,39 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NonReadOnlyUsers } from "../../../Utils/AuthorizeFor";
+import ButtonV2 from "../../Common/components/ButtonV2";
+import { navigate } from "raviger";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import InvestigationTab from "../Investigations/investigationsTab";
+import { t } from "i18next";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+export const ConsultationInvestigationsTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+
+
+ navigate(
+ `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}/investigation/`
+ )
+ }
+ >
+
+ {t("log_lab_results")}
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
new file mode 100644
index 00000000000..19810102833
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
@@ -0,0 +1,19 @@
+import { ConsultationTabProps } from "./index";
+import PrescriptionAdministrationsTable from "../../Medicine/PrescriptionAdministrationsTable";
+
+export const ConsultationMedicinesTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
new file mode 100644
index 00000000000..85f34f0ec63
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
@@ -0,0 +1,24 @@
+import { lazy } from "react";
+import { NeurologicalTable } from "../Consultations/NeurologicalTables";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeurologicalMonitoringTab = (
+ props: ConsultationTabProps
+) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
new file mode 100644
index 00000000000..69f130aca0d
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NutritionPlots } from "../Consultations/NutritionPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeutritionTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
new file mode 100644
index 00000000000..721ee18d13e
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NursingPlot } from "../Consultations/NursingPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNursingTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
new file mode 100644
index 00000000000..05327ca6870
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PressureSoreDiagrams } from "../Consultations/PressureSoreDiagrams";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationPressureSoreTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
new file mode 100644
index 00000000000..69d20dd64c9
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PrimaryParametersPlot } from "../Consultations/PrimaryParametersPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationSummaryTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
new file mode 100644
index 00000000000..705b3d9479a
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
@@ -0,0 +1,690 @@
+import { lazy, useEffect, useState } from "react";
+import { ConsultationTabProps } from "./index";
+import { AssetBedModel, AssetClass, AssetData } from "../../Assets/AssetTypes";
+import { useDispatch } from "react-redux";
+import { getPermittedFacility, listAssetBeds } from "../../../Redux/actions";
+import { BedModel, FacilityModel } from "../models";
+import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor";
+import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor";
+import useVitalsAspectRatioConfig from "../../VitalsMonitor/useVitalsAspectRatioConfig";
+import { DISCHARGE_REASONS, SYMPTOM_CHOICES } from "../../../Common/constants";
+import PrescriptionsTable from "../../Medicine/PrescriptionsTable";
+import Chip from "../../../CAREUI/display/Chip";
+import { formatDate, formatDateTime } from "../../../Utils/utils";
+import ReadMore from "../../Common/components/Readmore";
+import { DailyRoundsList } from "../Consultations/DailyRoundsList";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
+ const dispatch: any = useDispatch();
+ const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
+ const [hl7SocketUrl, setHL7SocketUrl] = useState();
+ const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
+ const [monitorBedData, setMonitorBedData] = useState();
+ const [ventilatorBedData, setVentilatorBedData] = useState();
+
+ const vitals = useVitalsAspectRatioConfig({
+ default: undefined,
+ md: 8 / 11,
+ lg: 15 / 11,
+ xl: 13 / 11,
+ "2xl": 19 / 11,
+ "3xl": 23 / 11,
+ });
+
+ useEffect(() => {
+ if (
+ !props.consultationData.facility ||
+ !props.consultationData.current_bed?.bed_object.id
+ )
+ return;
+
+ const fetchData = async () => {
+ const [facilityRes, assetBedRes] = await Promise.all([
+ dispatch(getPermittedFacility(props.consultationData.facility as any)),
+ dispatch(
+ listAssetBeds({
+ facility: props.consultationData.facility as any,
+ bed: props.consultationData.current_bed?.bed_object.id,
+ })
+ ),
+ ]);
+
+ const { middleware_address } = facilityRes.data as FacilityModel;
+ const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
+
+ const monitorBedData = assetBeds?.find(
+ (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
+ );
+ setMonitorBedData(monitorBedData);
+ const assetDataForMonitor = monitorBedData?.asset_object;
+ const hl7Meta = assetDataForMonitor?.meta;
+ const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
+ if (hl7Middleware && hl7Meta?.local_ip_address) {
+ setHL7SocketUrl(
+ `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
+ );
+ }
+
+ const consultationBedVentilator =
+ props.consultationData?.current_bed?.assets_objects?.find(
+ (i) => i.asset_class === AssetClass.VENTILATOR
+ );
+ let ventilatorBedData;
+ if (consultationBedVentilator) {
+ ventilatorBedData = {
+ asset_object: consultationBedVentilator,
+ bed_object: props.consultationData?.current_bed?.bed_object,
+ } as AssetBedModel;
+ } else {
+ ventilatorBedData = assetBeds?.find(
+ (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
+ );
+ }
+ setVentilatorBedData(ventilatorBedData);
+ const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
+ const ventilatorMiddleware =
+ ventilatorMeta?.middleware_hostname || middleware_address;
+ if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
+ setVentilatorSocketUrl(
+ `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
+ );
+ }
+
+ if (
+ !(hl7Middleware && hl7Meta?.local_ip_address) &&
+ !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
+ ) {
+ setHL7SocketUrl(undefined);
+ setVentilatorSocketUrl(undefined);
+ }
+ };
+
+ fetchData();
+ }, [props.consultationData]);
+
+ return (
+
+ {!props.consultationData.discharge_date &&
+ hl7SocketUrl &&
+ ventilatorSocketUrl && (
+
+ )}
+
+
+
+
+ {!props.consultationData.discharge_date &&
+ ((hl7SocketUrl && !ventilatorSocketUrl) ||
+ (!hl7SocketUrl && ventilatorSocketUrl)) && (
+
+ {(hl7SocketUrl || ventilatorSocketUrl) && (
+
+ {hl7SocketUrl && (
+
+
+
+ )}
+ {ventilatorSocketUrl && (
+
+
+
+ )}
+
+ )}
+
+ )}
+ {props.consultationData.discharge_date && (
+
+
+
+ Discharge Information
+
+
+
+ Reason {" - "}
+
+ {DISCHARGE_REASONS.find(
+ (d) =>
+ d.id === props.consultationData.discharge_reason
+ )?.text ?? "--"}
+
+
+ {props.consultationData.discharge_reason === "REF" && (
+
+ Referred Facility {" - "}
+
+ {props.consultationData.referred_to_external ||
+ props.consultationData.referred_to_object?.name ||
+ "--"}
+
+
+ )}
+ {props.consultationData.discharge_reason === "REC" && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Advice {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+
+
+
+ )}
+ {props.consultationData.discharge_reason === "EXP" && (
+
+
+ Date of Death {" - "}
+
+ {props.consultationData.death_datetime
+ ? formatDateTime(
+ props.consultationData.death_datetime
+ )
+ : "--:--"}
+
+
+
+ Cause of death {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ Confirmed By {" - "}
+
+ {props.consultationData.death_confirmed_doctor ??
+ "--"}
+
+
+
+ )}
+ {["REF", "LAMA"].includes(
+ props.consultationData.discharge_reason ?? ""
+ ) && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Notes {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ )}
+
+
+
+ )}
+ {props.consultationData.symptoms_text && (
+
+
+
+ Symptoms
+
+
+
+ Last Daily Update
+
+ {props.consultationData.last_daily_round
+ ?.additional_symptoms && (
+ <>
+
+ {props.consultationData.last_daily_round?.additional_symptoms.map(
+ (symptom: any, index: number) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.last_daily_round
+ ?.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {
+ props.consultationData.last_daily_round
+ ?.other_symptoms
+ }
+
+ )}
+
+ from{" "}
+ {formatDate(
+ props.consultationData.last_daily_round.created_at
+ )}
+
+ >
+ )}
+
+
+ Consultation Update
+
+
+ {props.consultationData.symptoms?.map(
+ (symptom, index) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {props.consultationData.other_symptoms}
+
+ )}
+
+ from{" "}
+ {props.consultationData.symptoms_onset_date
+ ? formatDate(props.consultationData.symptoms_onset_date)
+ : "--/--/----"}
+
+
+
+
+ )}
+
+ {props.consultationData.history_of_present_illness && (
+
+
+
+ History of Present Illness
+
+
+
+
+
+
+ )}
+
+ {props.consultationData.examination_details && (
+
+
+
+ Examination details and Clinical conditions:{" "}
+
+
+
+
+
+
+ )}
+ {props.consultationData.treatment_plan && (
+
+
+
+ Treatment Summary
+
+
+
+
+
+
+ )}
+ {props.consultationData.consultation_notes && (
+
+
+
+ General Instructions
+
+
+
+
+
+
+ )}
+
+ {(props.consultationData.operation ??
+ props.consultationData.special_instruction) && (
+
+
+
+ Notes
+
+
+ {props.consultationData.operation && (
+
+
Operation
+
+
+ )}
+
+ {props.consultationData.special_instruction && (
+
+
Special Instruction
+
+
+ )}
+
+
+
+ )}
+
+ {props.consultationData.procedure &&
+ props.consultationData.procedure.length > 0 && (
+
+
+
+
+
+
+ Procedure
+
+
+ Notes
+
+
+ Repetitive
+
+
+ Time / Frequency
+
+
+
+
+ {props.consultationData.procedure?.map(
+ (procedure, index) => (
+
+
+ {procedure.procedure}
+
+
+ {procedure.notes}
+
+
+ {procedure.repetitive ? "Yes" : "No"}
+
+
+ {procedure.repetitive
+ ? procedure.frequency
+ : formatDateTime(String(procedure.time))}
+
+
+ )
+ )}
+
+
+
+
+ )}
+ {props.consultationData.intubation_start_date && (
+
+
+
+ Date/Size/LL:{" "}
+
+
+
+ Intubation Date{" - "}
+
+ {formatDateTime(
+ props.consultationData.intubation_start_date
+ )}
+
+
+
+ Extubation Date{" - "}
+
+ {props.consultationData.intubation_end_date &&
+ formatDateTime(
+ props.consultationData.intubation_end_date
+ )}
+
+
+
+ ETT/TT (mmid){" - "}
+
+ {props.consultationData.ett_tt}
+
+
+
+ Cuff Pressure (mmhg){" - "}
+
+ {props.consultationData.cuff_pressure}
+
+
+
+
+
+ )}
+
+ {props.consultationData.lines?.length > 0 && (
+
+
+
+ Lines and Catheters
+
+
+ {props.consultationData.lines?.map(
+ (line: any, idx: number) => (
+
+
{line.type}
+
+ Details:
+
+ {line.other_type}
+
+
+ Insertion Date:{" "}
+
+ {formatDateTime(line.start_date)}
+
+
+
+ Site/Level of Fixation:
+
+ {line.site}
+
+
+
+ )
+ )}
+
+
+
+ )}
+
+
+
+
+ Body Details
+
+
+
+ Gender {" - "}
+
+ {props.patientData.gender ?? "-"}
+
+
+
+ Age {" - "}
+
+ {props.patientData.age ?? "-"}
+
+
+
+ Weight {" - "}
+
+ {props.consultationData.weight ?? "-"} Kg
+
+
+
+ Height {" - "}
+
+ {props.consultationData.height ?? "-"} cm
+
+
+
+ Body Surface Area {" - "}
+
+ {Math.sqrt(
+ (Number(props.consultationData.weight) *
+ Number(props.consultationData.height)) /
+ 3600
+ ).toFixed(2)}{" "}
+ m2
+
+
+
+ Blood Group {" - "}
+
+ {props.patientData.blood_group ?? "-"}
+
+
+
+
+
+
+
+
+
+
+ setShowAutomatedRounds((s) => !s)}
+ />
+
+ Show Automated Rounds
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
new file mode 100644
index 00000000000..d14b54cc096
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { VentilatorPlot } from "../Consultations/VentilatorPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationVentilatorTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx
new file mode 100644
index 00000000000..affdb2756cd
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/index.tsx
@@ -0,0 +1,501 @@
+import {
+ CONSULTATION_TABS,
+ GENDER_TYPES,
+ OptionsType,
+ SYMPTOM_CHOICES,
+} from "../../../Common/constants";
+import { ConsultationModel, ICD11DiagnosisModel } from "../models";
+import { getConsultation, getPatient } from "../../../Redux/actions";
+import { statusType, useAbortableEffect } from "../../../Common/utils";
+import { lazy, useCallback, useState } from "react";
+
+import ButtonV2 from "../../Common/components/ButtonV2";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import DischargeModal from "../DischargeModal";
+import DischargeSummaryModal from "../DischargeSummaryModal";
+import DoctorVideoSlideover from "../DoctorVideoSlideover";
+import { make as Link } from "../../Common/components/Link.bs";
+import PatientInfoCard from "../../Patient/PatientInfoCard";
+import { PatientModel } from "../../Patient/models";
+import { formatDateTime, relativeTime } from "../../../Utils/utils";
+
+import { navigate } from "raviger";
+import { useDispatch } from "react-redux";
+import { useQueryParams } from "raviger";
+import { useTranslation } from "react-i18next";
+import { triggerGoal } from "../../Common/Plausible";
+import useAuthUser from "../../../Common/hooks/useAuthUser";
+import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab";
+import { ConsultationABGTab } from "./ConsultationABGTab";
+import { ConsultationNursingTab } from "./ConsultationNursingTab";
+import { ConsultationFeedTab } from "./ConsultationFeedTab";
+import { ConsultationSummaryTab } from "./ConsultationSummaryTab";
+import { ConsultationFilesTab } from "./ConsultationFilesTab";
+import { ConsultationMedicinesTab } from "./ConsultationMedicinesTab";
+import { ConsultationInvestigationsTab } from "./ConsultationInvestigationsTab";
+import { ConsultationVentilatorTab } from "./ConsultationVentilatorTab";
+import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab";
+import { ConsultationDialysisTab } from "./ConsultationDialysisTab";
+import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab";
+
+const Loading = lazy(() => import("../../Common/Loading"));
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+const symptomChoices = [...SYMPTOM_CHOICES];
+
+export interface ConsultationTabProps {
+ consultationId: string;
+ facilityId: string;
+ patientId: string;
+ consultationData: ConsultationModel;
+ patientData: PatientModel;
+}
+
+export const ConsultationDetails = (props: any) => {
+ const { t } = useTranslation();
+ const { facilityId, patientId, consultationId } = props;
+ const tab = props.tab.toUpperCase();
+ const dispatch: any = useDispatch();
+ const [isLoading, setIsLoading] = useState(false);
+ const [showDoctors, setShowDoctors] = useState(false);
+ const [qParams, _] = useQueryParams();
+
+ const [consultationData, setConsultationData] = useState(
+ {} as ConsultationModel
+ );
+ const [patientData, setPatientData] = useState({});
+ const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
+ useState(false);
+ const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
+
+ const getPatientGender = (patientData: any) =>
+ GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
+
+ const getPatientAddress = (patientData: any) =>
+ `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
+
+ const getPatientComorbidities = (patientData: any) => {
+ if (patientData?.medical_history?.length) {
+ const medHis = patientData.medical_history;
+ return medHis.map((item: any) => item.disease).join(", ");
+ } else {
+ return "None";
+ }
+ };
+
+ const authUser = useAuthUser();
+
+ const fetchData = useCallback(
+ async (status: statusType) => {
+ setIsLoading(true);
+ const res = await dispatch(getConsultation(consultationId));
+ if (!status.aborted) {
+ if (res?.data) {
+ const data: ConsultationModel = {
+ ...res.data,
+ symptoms_text: "",
+ };
+ if (res.data.symptoms?.length) {
+ const symptoms = res.data.symptoms
+ .filter((symptom: number) => symptom !== 9)
+ .map((symptom: number) => {
+ const option = symptomChoices.find((i) => i.id === symptom);
+ return option ? option.text.toLowerCase() : symptom;
+ });
+ data.symptoms_text = symptoms.join(", ");
+ }
+ setConsultationData(data);
+ const id = res.data.patient;
+ const patientRes = await dispatch(getPatient({ id }));
+ if (patientRes?.data) {
+ const patientGender = getPatientGender(patientRes.data);
+ const patientAddress = getPatientAddress(patientRes.data);
+ const patientComorbidities = getPatientComorbidities(
+ patientRes.data
+ );
+ const data = {
+ ...patientRes.data,
+ gender: patientGender,
+ address: patientAddress,
+ comorbidities: patientComorbidities,
+ is_declared_positive: patientRes.data.is_declared_positive
+ ? "Yes"
+ : "No",
+ is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
+ };
+ setPatientData(data);
+ }
+ } else {
+ navigate("/not-found");
+ }
+ setIsLoading(false);
+ }
+ },
+ [consultationId, dispatch, patientData.is_vaccinated]
+ );
+
+ useAbortableEffect((status: statusType) => {
+ fetchData(status);
+ triggerGoal("Patient Consultation Viewed", {
+ facilityId: facilityId,
+ consultationId: consultationId,
+ userId: authUser.id,
+ });
+ }, []);
+
+ const TABS = {
+ UPDATES: ConsultationUpdatesTab,
+ FEED: ConsultationFeedTab,
+ SUMMARY: ConsultationSummaryTab,
+ MEDICINES: ConsultationMedicinesTab,
+ FILES: ConsultationFilesTab,
+ INVESTIGATIONS: ConsultationInvestigationsTab,
+ ABG: ConsultationABGTab,
+ NURSING: ConsultationNursingTab,
+ NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab,
+ VENTILATOR: ConsultationVentilatorTab,
+ NUTRITION: ConsultationNursingTab,
+ PRESSURE_SORE: ConsultationPressureSoreTab,
+ DIALYSIS: ConsultationDialysisTab,
+ };
+
+ const consultationTabProps: ConsultationTabProps = {
+ consultationId,
+ facilityId,
+ patientId,
+ consultationData,
+ patientData,
+ };
+
+ const SelectedTab = TABS[tab];
+
+ if (isLoading) {
+ return ;
+ }
+
+ const tabButtonClasses = (selected: boolean) =>
+ `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
+ selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
+ }`;
+
+ const ShowDiagnosis = ({
+ diagnoses = [],
+ label = "Diagnosis",
+ nshow = 2,
+ }: {
+ diagnoses: ICD11DiagnosisModel[] | undefined;
+ label: string;
+ nshow?: number;
+ }) => {
+ const [showMore, setShowMore] = useState(false);
+
+ return diagnoses.length ? (
+
+ ) : null;
+ };
+
+ return (
+
+
setOpenDischargeSummaryDialog(false)}
+ />
+
+ setOpenDischargeDialog(false)}
+ consultationData={consultationData}
+ />
+
+
+
+
+
+ {!consultationData.discharge_date && (
+
+
+ navigate(
+ `/facility/${patientData.facility}/patient/${patientData.id}/shift/new`
+ )
+ }
+ className="btn btn-primary m-1 w-full hover:text-white"
+ >
+
+ Shift Patient
+
+ {
+ triggerGoal("Doctor Connect Clicked", {
+ consultationId,
+ facilityId: patientData.facility,
+ userId: authUser.id,
+ page: "ConsultationDetails",
+ });
+ setShowDoctors(true);
+ }}
+ className="btn btn-primary m-1 w-full hover:text-white"
+ >
+ Doctor Connect
+
+ {patientData.last_consultation?.id && (
+
+ Camera Feed
+
+ )}
+
+ )}
+
+
+ Patient Details
+
+
+ Doctor's Notes
+
+
+
+
+
+
+
+
+
+ {consultationData.admitted_to && (
+
+
+ Patient
+ {consultationData.discharge_date
+ ? " Discharged from"
+ : " Admitted to"}
+
+ {consultationData.admitted_to}
+
+
+ {(consultationData.admission_date ??
+ consultationData.discharge_date) && (
+
+ {relativeTime(
+ consultationData.discharge_date
+ ? consultationData.discharge_date
+ : consultationData.admission_date
+ )}
+
+ )}
+
+ {consultationData.admission_date &&
+ formatDateTime(consultationData.admission_date)}
+ {consultationData.discharge_date &&
+ ` - ${formatDateTime(consultationData.discharge_date)}`}
+
+
+ )}
+
+
+
+
+ {/*consultationData.other_symptoms && (
+
+
+ Other Symptoms:{" "}
+
+ {consultationData.other_symptoms}
+
+ )*/}
+
+ {consultationData.icd11_principal_diagnosis && (
+
+ d.id === consultationData.icd11_principal_diagnosis
+ )!,
+ ]}
+ />
+ )}
+
+
+
+
+
+ {(consultationData.verified_by_object ||
+ consultationData.deprecated_verified_by) && (
+
+
+ Verified By:{" "}
+
+ {consultationData.verified_by_object
+ ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
+ : consultationData.deprecated_verified_by}
+
+
+ )}
+
+
+ setOpenDischargeSummaryDialog(true)}>
+
+ {t("discharge_summary")}
+
+
+ setOpenDischargeDialog(true)}
+ disabled={!!consultationData.discharge_date}
+ >
+
+ {t("discharge_from_care")}
+
+
+
+
+
+
+ Created:
+ {consultationData.created_date
+ ? formatDateTime(consultationData.created_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.created_by && (
+
+ {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
+ {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
+
+ )}
+
+
+
+ Last Modified:
+ {consultationData.modified_date
+ ? formatDateTime(consultationData.modified_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.last_edited_by && (
+
+ {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
+ {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
+
+ )}
+
+
+
+
+
+
+
+
+
+ {CONSULTATION_TABS.map((p: OptionsType) => {
+ if (p.text === "FEED") {
+ if (
+ !consultationData?.current_bed?.bed_object?.id ??
+ consultationData?.discharge_date !== null
+ )
+ return null;
+ }
+ return (
+
+ {p.desc}
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+ );
+};
From ac9a4ee6a60bce858e0eaa56a0f47380f8d255df Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Mon, 11 Sep 2023 19:19:44 +0530
Subject: [PATCH 22/50] Add validation for average working hours (#6243)
* Add validation for average working hours
* Fix error message
---
src/Components/Users/ManageUsers.tsx | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx
index d2e0ca415e3..8625d442f52 100644
--- a/src/Components/Users/ManageUsers.tsx
+++ b/src/Components/Users/ManageUsers.tsx
@@ -73,6 +73,8 @@ export default function ManageUsers() {
name: string;
}>({ show: false, username: "", name: "" });
+ const [weeklyHoursError, setWeeklyHoursError] = useState("");
+
const extremeSmallScreenBreakpoint = 320;
const isExtremeSmallScreen =
width <= extremeSmallScreenBreakpoint ? true : false;
@@ -144,7 +146,10 @@ export default function ManageUsers() {
const handleWorkingHourSubmit = async () => {
const username = selectedUser;
- if (!username || weeklyHours < 0 || weeklyHours > 168) return;
+ if (!username || !weeklyHours || weeklyHours < 0 || weeklyHours > 168) {
+ setWeeklyHoursError("Value should be between 0 and 168");
+ return;
+ }
const res = await dispatch(
partialUpdateUser(username, {
weekly_working_hours: weeklyHours,
@@ -163,6 +168,7 @@ export default function ManageUsers() {
});
}
setWeeklyHours(0);
+ setWeeklyHoursError("");
fetchData({ aborted: false });
};
@@ -493,13 +499,14 @@ export default function ManageUsers() {
{
+ setExpandWorkingHours(state);
+ setWeeklyHours(0);
+ setWeeklyHoursError("");
+ }}
slideFrom="right"
title="Average weekly working hours"
dialogClass="md:w-[400px]"
- onCloseClick={() => {
- setWeeklyHours(0);
- }}
>
@@ -512,11 +519,7 @@ export default function ManageUsers() {
onChange={(e) => {
setWeeklyHours(e.value);
}}
- error={
- weeklyHours < 0 || weeklyHours > 168
- ? "Average weekly working hours should be between 0 and 168"
- : ""
- }
+ error={weeklyHoursError}
required
label=""
type="number"
From 3d73c69b4d9f8ca094bbc93407274a3b80da53a6 Mon Sep 17 00:00:00 2001
From: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com>
Date: Mon, 11 Sep 2023 19:20:41 +0530
Subject: [PATCH 23/50] changed priority of backUrl and history for back button
in pageTitle (#6251)
---
src/Common/hooks/useAppHistory.ts | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/Common/hooks/useAppHistory.ts b/src/Common/hooks/useAppHistory.ts
index 893b1a06cfd..70ad92692de 100644
--- a/src/Common/hooks/useAppHistory.ts
+++ b/src/Common/hooks/useAppHistory.ts
@@ -10,14 +10,12 @@ export default function useAppHistory() {
const resetHistory = useContext(ResetHistoryContext);
const goBack = (fallbackUrl?: string) => {
- if (history.length > 1)
- // Navigate to history present in the app navigation history stack.
- return navigate(history[1]);
-
if (fallbackUrl)
- // Otherwise, use provided fallback url if provided.
+ // use provided fallback url if provided.
return navigate(fallbackUrl);
-
+ if (history.length > 1)
+ // Otherwise, navigate to history present in the app navigation history stack.
+ return navigate(history[1]);
// Otherwise, fallback to browser's go back behaviour.
window.history.back();
};
From baecaab94b617302fb20cf4aa7766325defb8870 Mon Sep 17 00:00:00 2001
From: print-Sathvik <113630200+print-Sathvik@users.noreply.github.com>
Date: Mon, 11 Sep 2023 19:21:27 +0530
Subject: [PATCH 24/50] Asset filter parameters reset on unselect (#6255)
---
src/Components/Assets/AssetFilter.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Components/Assets/AssetFilter.tsx b/src/Components/Assets/AssetFilter.tsx
index b0a870d0e31..299d67e5ab0 100644
--- a/src/Components/Assets/AssetFilter.tsx
+++ b/src/Components/Assets/AssetFilter.tsx
@@ -108,9 +108,9 @@ function AssetFilter(props: any) {
const applyFilter = () => {
const data = {
facility: facilityId,
- asset_type: asset_type,
- asset_class: asset_class,
- status: asset_status,
+ asset_type: asset_type ?? "",
+ asset_class: asset_class ?? "",
+ status: asset_status ?? "",
location: locationId,
};
onChange(data);
From 4e8005c96bda7baa03442d21f5b18bb592426d59 Mon Sep 17 00:00:00 2001
From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com>
Date: Mon, 11 Sep 2023 19:23:25 +0530
Subject: [PATCH 25/50] Make the form consistent (#6238)
* shift right
* Update src/Components/Facility/AddLocationForm.tsx
Co-authored-by: Rithvik Nishad
---------
Co-authored-by: Rithvik Nishad
---
src/Components/Facility/AddLocationForm.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Components/Facility/AddLocationForm.tsx b/src/Components/Facility/AddLocationForm.tsx
index d3c64ec9e04..f0dd7893aca 100644
--- a/src/Components/Facility/AddLocationForm.tsx
+++ b/src/Components/Facility/AddLocationForm.tsx
@@ -158,7 +158,7 @@ export const AddLocationForm = (props: LocationFormProps) => {
/>
-
+
navigate(`/facility/${facilityId}/location`, {
From 7cda789169e188d377134596aaae81731a523acd Mon Sep 17 00:00:00 2001
From: "Tasnimul H. Tauhid"
Date: Mon, 11 Sep 2023 19:25:00 +0530
Subject: [PATCH 26/50] Disabled asset config for user other than admin (#6259)
---
src/Components/Assets/AssetManage.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx
index 825cee1c848..25f879ddd09 100644
--- a/src/Components/Assets/AssetManage.tsx
+++ b/src/Components/Assets/AssetManage.tsx
@@ -29,7 +29,7 @@ import { useTranslation } from "react-i18next";
const PageTitle = lazy(() => import("../Common/PageTitle"));
const Loading = lazy(() => import("../Common/Loading"));
import * as Notification from "../../Utils/Notifications.js";
-import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
+import AuthorizeFor, { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
import Uptime from "../Common/Uptime";
import useAuthUser from "../../Common/hooks/useAuthUser";
import dayjs from "dayjs";
@@ -452,7 +452,7 @@ const AssetManage = (props: AssetManageProps) => {
}
id="configure-asset"
data-testid="asset-configure-button"
- authorizeFor={NonReadOnlyUsers}
+ authorizeFor={AuthorizeFor(["DistrictAdmin", "StateAdmin"])}
>
{t("configure")}
From 2da1eeeee8788e6c38d5ba1a590da8cd0ee3ae50 Mon Sep 17 00:00:00 2001
From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com>
Date: Tue, 12 Sep 2023 09:36:39 +0530
Subject: [PATCH 27/50] add test to search asset by qr and serial number
(#6266)
* add search by qr and ID
* made variables values dynamic
---
.../{asset_tab.cy.ts => asset_homepage.cy.ts} | 28 +++++++++--
cypress/pageobject/Asset/AssetSearch.ts | 47 +++++++++++++++++--
2 files changed, 65 insertions(+), 10 deletions(-)
rename cypress/e2e/assets_spec/{asset_tab.cy.ts => asset_homepage.cy.ts} (55%)
diff --git a/cypress/e2e/assets_spec/asset_tab.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts
similarity index 55%
rename from cypress/e2e/assets_spec/asset_tab.cy.ts
rename to cypress/e2e/assets_spec/asset_homepage.cy.ts
index 0abf11fcaf0..6e9ceb9676b 100644
--- a/cypress/e2e/assets_spec/asset_tab.cy.ts
+++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts
@@ -5,15 +5,21 @@ import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan";
import { AssetPagination } from "../../pageobject/Asset/AssetPagination";
import { AssetFilters } from "../../pageobject/Asset/AssetFilters";
+import LoginPage from "../../pageobject/Login/LoginPage";
+import { v4 as uuidv4 } from "uuid";
describe("Asset Tab", () => {
const assetSearchPage = new AssetSearchPage();
const assetQRScanPage = new AssetQRScanPage();
const assetPagination = new AssetPagination();
const assetFilters = new AssetFilters();
+ const loginPage = new LoginPage();
+ const assetName = "Dummy Camera 10";
+ const qrCode = uuidv4();
+ const serialNumber = Math.floor(Math.random() * 10 ** 10).toString();
before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
+ loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
});
@@ -24,11 +30,23 @@ describe("Asset Tab", () => {
// search for a element
- it("Search Asset Name", () => {
- const initialUrl = cy.url();
- assetSearchPage.typeSearchKeyword("dummy camera 30");
+ it("Search Asset Name/QR_ID/Serial_number", () => {
+ assetSearchPage.typeSearchKeyword(assetName);
assetSearchPage.pressEnter();
- assetSearchPage.verifyUrlChanged(initialUrl);
+ assetSearchPage.verifyBadgeContent(assetName);
+ assetSearchPage.clickAssetByName(assetName);
+ assetSearchPage.clickUpdateButton();
+ assetSearchPage.clearAndTypeQRCode(qrCode);
+ assetSearchPage.clearAndTypeSerialNumber(serialNumber);
+ assetSearchPage.clickAssetSubmitButton();
+ assetSearchPage.visitAssetsPage();
+ assetSearchPage.typeSearchKeyword(qrCode);
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(qrCode);
+ assetSearchPage.typeSearchKeyword(serialNumber);
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(serialNumber);
});
// scan a asset qr code
diff --git a/cypress/pageobject/Asset/AssetSearch.ts b/cypress/pageobject/Asset/AssetSearch.ts
index 315a414a62b..fea6a975983 100644
--- a/cypress/pageobject/Asset/AssetSearch.ts
+++ b/cypress/pageobject/Asset/AssetSearch.ts
@@ -1,16 +1,22 @@
export class AssetSearchPage {
typeSearchKeyword(keyword: string) {
- cy.get("[name='search']").type(keyword);
+ cy.get("#search").clear();
+ cy.get("#search").click().type(keyword);
}
pressEnter() {
cy.get("[name='search']").type("{enter}");
}
- verifyUrlChanged(initialUrl: string) {
- cy.url().should((currentUrl) => {
- expect(currentUrl).not.to.equal(initialUrl);
- });
+ clickAssetByName(assetName: string) {
+ cy.get("[data-testid='created-asset-list']").contains(assetName).click();
+ }
+
+ verifyBadgeContent(expectedText: string) {
+ cy.get("[data-testid='Name/Serial No./QR ID']").should(
+ "contain",
+ expectedText
+ );
}
verifyAssetIsPresent(assetName: string) {
@@ -18,4 +24,35 @@ export class AssetSearchPage {
.first()
.should("contain", assetName);
}
+
+ clickUpdateButton() {
+ cy.get("[data-testid='asset-update-button']").contains("Update").click();
+ }
+
+ clearAndTypeQRCode(qrCode: string) {
+ cy.get("#qr_code_id").clear();
+ cy.get("#qr_code_id").click().type(qrCode);
+ }
+
+ clearAndTypeSerialNumber(serialNumber: string) {
+ cy.get("#serial-number").clear();
+ cy.get("#serial-number").click().type(serialNumber);
+ }
+
+ clickAssetSubmitButton() {
+ cy.intercept("GET", "**/api/v1/asset/**").as("getAssets");
+ cy.get("#submit").click();
+ cy.wait("@getAssets").its("response.statusCode").should("eq", 200);
+ }
+
+ visitAssetsPage() {
+ cy.visit("/assets");
+ }
+
+ verifyAssetListContains(dummyCameraText: string) {
+ cy.get("[data-testid='created-asset-list']").should(
+ "contain",
+ dummyCameraText
+ );
+ }
}
From 203288eaae5392cb4ad4bfd49205233efa1119c2 Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Tue, 12 Sep 2023 10:27:31 +0530
Subject: [PATCH 28/50] remove quotes from `.env`, gitignore bun lock file
(#6263)
---
.env | 8 ++++----
.gitignore | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/.env b/.env
index fcabed6db34..61d4177305c 100644
--- a/.env
+++ b/.env
@@ -1,11 +1,11 @@
# Whitelabelling envs
-REACT_APP_TITLE="CARE"
-REACT_APP_META_DESCRIPTION="CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations."
+REACT_APP_TITLE=CARE
+REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations.
REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg
REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg
-REACT_APP_CONFIG=""
-REACT_PUBLIC_URL="https://care.coronasafe.in"
+REACT_APP_CONFIG=
+REACT_PUBLIC_URL=https://care.coronasafe.in
# Dev envs
ESLINT_NO_DEV_ERRORS=true
diff --git a/.gitignore b/.gitignore
index b97356ed20f..4ca589aab39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@ public/build-meta.json
# Using NPM
yarn.lock
pnpm-lock.yaml
+bun.lockb
# Cypress
cypress/downloads
From f7ac3338956bff973afeeec5c4ec6e8eb5eca35d Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Tue, 12 Sep 2023 10:39:11 +0530
Subject: [PATCH 29/50] remove `REACT_APP_CONFIG` from `.env` (#6268)
---
.env | 1 -
src/Redux/api.tsx | 2 +-
src/vite-env.d.ts | 2 +-
3 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/.env b/.env
index 61d4177305c..38fd950b0d8 100644
--- a/.env
+++ b/.env
@@ -4,7 +4,6 @@ REACT_APP_TITLE=CARE
REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations.
REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg
REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg
-REACT_APP_CONFIG=
REACT_PUBLIC_URL=https://care.coronasafe.in
# Dev envs
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 393b9372ca5..01a91d4ce3a 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -10,7 +10,7 @@ interface Routes {
const routes: Routes = {
config: {
- path: import.meta.env.REACT_APP_CONFIG || "/config.json",
+ path: import.meta.env.REACT_APP_CONFIG ?? "/config.json",
method: "GET",
noAuth: true,
},
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 529c2689aed..c899d453626 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -5,7 +5,7 @@ interface ImportMetaEnv {
readonly REACT_APP_META_DESCRIPTION: string;
readonly REACT_APP_COVER_IMAGE: string;
readonly REACT_APP_COVER_IMAGE_ALT: string;
- readonly REACT_APP_CONFIG: string;
+ readonly REACT_APP_CONFIG: string | undefined;
readonly REACT_PUBLIC_URL: string;
readonly REACT_APP_SITE_URL: string;
readonly REACT_APP_ANALYTICS_SERVER_URL: string;
From dd1dcc3e5af7cdf9ced3b71c264614d78614243a Mon Sep 17 00:00:00 2001
From: Aakash Singh
Date: Tue, 12 Sep 2023 15:16:48 +0530
Subject: [PATCH 30/50] fix load dummy data command (#6270)
* fix load dummy data command
* fix
---
.github/workflows/cypress.yaml | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml
index 4f23fbe4937..f66f9a37bd8 100644
--- a/.github/workflows/cypress.yaml
+++ b/.github/workflows/cypress.yaml
@@ -25,12 +25,13 @@ jobs:
path: care
- name: Run docker compose up on care ๐ณ
- run: cd care && touch .env && make docker_config_file=docker-compose.pre-built.yaml up && cd .. && sleep 60s
- # Voluntarily kept 60 seconds delay to wait for migrations to complete.
-
- - name: Run Django collectstatic and load dummy data on care ๐
run: |
- docker exec care python manage.py load_dummy_data
+ cd care
+ make docker_config_file=docker-compose.pre-built.yaml up
+ sleep 60s
+ docker compose exec backend bash -c "python manage.py load_dummy_data"
+ cd ..
+ # Voluntarily kept 60 seconds delay to wait for migrations to complete.
- name: Check care is up โป
run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000
From d0ffbd5955f34755d077ff8d57e2e5c22ceafe8b Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 12 Sep 2023 17:40:58 +0530
Subject: [PATCH 31/50] Refactor workflow to properly wait for migrations
(#6272)
* Refactor workflow to properly wait for migrations
* use while
* Fix command and remove cypress videos
---
.github/workflows/cypress.yaml | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml
index f66f9a37bd8..130f360d2ef 100644
--- a/.github/workflows/cypress.yaml
+++ b/.github/workflows/cypress.yaml
@@ -24,14 +24,22 @@ jobs:
repository: coronasafe/care
path: care
- - name: Run docker compose up on care ๐ณ
+ - name: Start care docker containers ๐ณ
run: |
- cd care
+ cd care
make docker_config_file=docker-compose.pre-built.yaml up
- sleep 60s
+ while docker compose exec backend bash -c "python manage.py showmigrations 2>/dev/null | cat | grep -q '\[ \]'"; do
+ >&2 echo "Migrations are not yet applied - sleeping"
+ sleep 5
+ done
+ echo "Migrations are applied"
+ cd ..
+
+ - name: Load dummy data into care backend ๐
+ run: |
+ cd care
docker compose exec backend bash -c "python manage.py load_dummy_data"
cd ..
- # Voluntarily kept 60 seconds delay to wait for migrations to complete.
- name: Check care is up โป
run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000
@@ -93,10 +101,3 @@ jobs:
name: cypress-screenshots
path: cypress/screenshots
- # Test run video was always captured, so this action uses "always()" condition
- - name: Upload cypress videos ๐น
- uses: actions/upload-artifact@v3
- if: always()
- with:
- name: cypress-videos
- path: cypress/videos
From 8c242d650395990170ef5ee21b8a104c89bf4f51 Mon Sep 17 00:00:00 2001
From: Ashesh <3626859+Ashesh3@users.noreply.github.com>
Date: Tue, 12 Sep 2023 19:21:04 +0530
Subject: [PATCH 32/50] Asset CSV export (#6262)
---
src/Common/constants.tsx | 2 +-
src/Components/Assets/AssetsList.tsx | 20 ++++++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx
index e8ab4867764..72a939b92fd 100644
--- a/src/Common/constants.tsx
+++ b/src/Common/constants.tsx
@@ -993,7 +993,7 @@ export const XLSXAssetImportSchema = {
return ip;
},
},
- "Config: Camera Access Key": {
+ "Config - Camera Access Key": {
prop: "camera_access_key",
type: String,
},
diff --git a/src/Components/Assets/AssetsList.tsx b/src/Components/Assets/AssetsList.tsx
index 6183a47783c..3789d7cfd51 100644
--- a/src/Components/Assets/AssetsList.tsx
+++ b/src/Components/Assets/AssetsList.tsx
@@ -324,7 +324,7 @@ const AssetsList = () => {
},
},
{
- label: "Export Assets",
+ label: "Export Assets (JSON)",
action: () =>
authorizedForImportExport &&
listAssets({
@@ -333,7 +333,23 @@ const AssetsList = () => {
limit: totalCount,
}),
type: "json",
- filePrefix: `assets_${facility?.name}`,
+ filePrefix: `assets_${facility?.name ?? "all"}`,
+ options: {
+ icon: ,
+ disabled: totalCount === 0 || !authorizedForImportExport,
+ },
+ },
+ {
+ label: "Export Assets (CSV)",
+ action: () =>
+ authorizedForImportExport &&
+ listAssets({
+ ...qParams,
+ csv: true,
+ limit: totalCount,
+ }),
+ type: "csv",
+ filePrefix: `assets_${facility?.name ?? "all"}`,
options: {
icon: ,
disabled: totalCount === 0 || !authorizedForImportExport,
From 87f36ba0332f69fce09ef08dc182449513fa2059 Mon Sep 17 00:00:00 2001
From: Rithvik Nishad
Date: Tue, 12 Sep 2023 19:24:12 +0530
Subject: [PATCH 33/50] add readmission (#6239)
---
src/Components/Facility/ConsultationCard.tsx | 10 ++++++++++
src/Components/Facility/models.tsx | 1 +
src/Components/Patient/ManagePatients.tsx | 9 +++++++++
src/Components/Patient/PatientInfoCard.tsx | 17 +++++++++++++----
4 files changed, 33 insertions(+), 4 deletions(-)
diff --git a/src/Components/Facility/ConsultationCard.tsx b/src/Components/Facility/ConsultationCard.tsx
index 467729f0f73..f6b4484b477 100644
--- a/src/Components/Facility/ConsultationCard.tsx
+++ b/src/Components/Facility/ConsultationCard.tsx
@@ -5,6 +5,7 @@ import ButtonV2 from "../Common/components/ButtonV2";
import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
import RelativeDateUserMention from "../Common/RelativeDateUserMention";
import useConfig from "../../Common/hooks/useConfig";
+import Chip from "../../CAREUI/display/Chip";
interface ConsultationProps {
itemData: ConsultationModel;
@@ -70,6 +71,15 @@ export const ConsultationCard = (props: ConsultationProps) => {
{formatDateTime(itemData.admission_date)}
+ {itemData.is_readmission && (
+
+ )}