@openmrs/esm-billing-app 1.1.2-pre.1621 → 1.1.2-pre.1630
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/1807.js +1 -1
- package/dist/3002.js +1 -1
- package/dist/3857.js +1 -1
- package/dist/4300.js +1 -1
- package/dist/6792.js +1 -1
- package/dist/8341.js +2 -0
- package/dist/8341.js.map +1 -0
- package/dist/8421.js +1 -1
- package/dist/8421.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +1 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +41 -41
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/billing-form/billing-checkin-form.component.tsx +115 -55
- package/src/billing-form/billing-checkin-form.scss +26 -2
- package/src/billing-form/billing-checkin-form.test.tsx +51 -1
- package/src/billing-form/billing-form.resource.test.ts +87 -0
- package/src/billing-form/billing-form.resource.ts +33 -0
- package/src/billing-form/visit-attributes/visit-attributes-form.component.tsx +25 -2
- package/src/billing-form/visit-attributes/visit-attributes-form.scss +29 -0
- package/tools/setup-tests.ts +7 -6
- package/translations/en.json +7 -0
- package/dist/3057.js +0 -2
- package/dist/3057.js.map +0 -1
- /package/dist/{3057.js.LICENSE.txt → 8341.js.LICENSE.txt} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
var _openmrs_esm_billing_app;(()=>{"use strict";var e,r,t,n,o,a,i,l,s,u,f,p,d,c,h,m,v,g,b,y,w,_={45120(e,r,t){var n={"./start":()=>Promise.all([t.e(2177),t.e(
|
|
1
|
+
var _openmrs_esm_billing_app;(()=>{"use strict";var e,r,t,n,o,a,i,l,s,u,f,p,d,c,h,m,v,g,b,y,w,_={45120(e,r,t){var n={"./start":()=>Promise.all([t.e(2177),t.e(8341),t.e(1435),t.e(6072),t.e(7321),t.e(8421)]).then((()=>()=>t(18421)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),a=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>a})}},P={};function S(e){var r=P[e];if(void 0!==r)return r.exports;var t=P[e]={id:e,loaded:!1,exports:{}};return _[e].call(t.exports,t,t.exports,S),t.loaded=!0,t.exports}S.m=_,S.c=P,S.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return S.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,S.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);S.r(o);var a={};e=e||[null,r({}),r([]),r(r)];for(var i=2&n&&t;("object"==typeof i||"function"==typeof i)&&!~e.indexOf(i);i=r(i))Object.getOwnPropertyNames(i).forEach((e=>a[e]=()=>t[e]));return a.default=()=>t,S.d(o,a),o},S.d=(e,r)=>{for(var t in r)S.o(r,t)&&!S.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},S.f={},S.e=e=>Promise.all(Object.keys(S.f).reduce(((r,t)=>(S.f[t](e,r),r)),[])),S.u=e=>e+".js",S.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@openmrs/esm-billing-app:",S.l=(e,r,o,a)=>{if(t[e])t[e].push(r);else{var i,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var f=s[u];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+o){i=f;break}}i||(l=!0,(i=document.createElement("script")).charset="utf-8",S.nc&&i.setAttribute("nonce",S.nc),i.setAttribute("data-webpack",n+o),i.src=e),t[e]=[r];var p=(r,n)=>{i.onerror=i.onload=null,clearTimeout(d);var o=t[e];if(delete t[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(n))),r)return r(n)},d=setTimeout(p.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=p.bind(null,i.onerror),i.onload=p.bind(null,i.onload),l&&document.head.appendChild(i)}},S.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},S.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{S.S={};var e={},r={};S.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];S.o(S.S,t)||(S.S[t]={});var a=S.S[t],i="@openmrs/esm-billing-app",l=(e,r,t,n)=>{var o=a[e]=a[e]||{},l=o[r];(!l||!l.loaded&&(!n!=!l.eager?n:i>l.from))&&(o[r]={get:t,from:i,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","9.0.3-pre.4453",(()=>Promise.all([S.e(2177),S.e(6792),S.e(8341),S.e(6072),S.e(1997),S.e(7321)]).then((()=>()=>S(46792))))),l("react-dom","18.3.1",(()=>Promise.all([S.e(961),S.e(6072)]).then((()=>()=>S(40961))))),l("react-i18next","16.0.0",(()=>Promise.all([S.e(7255),S.e(6072)]).then((()=>()=>S(77255))))),l("react","18.3.1",(()=>S.e(6540).then((()=>()=>S(96540))))),l("rxjs","6.6.7",(()=>S.e(3184).then((()=>()=>S(63184))))),l("swr/immutable","2.3.3",(()=>Promise.all([S.e(2177),S.e(6072),S.e(4225)]).then((()=>()=>S(54225))))),l("swr/infinite","2.3.3",(()=>Promise.all([S.e(2177),S.e(6072),S.e(3041)]).then((()=>()=>S(23041)))))),e[t]=s.length?Promise.all(s).then((()=>e[t]=1)):1}}})(),(()=>{var e;globalThis.importScripts&&(e=globalThis.location+"");var r=globalThis.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),S.p=e})(),o=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},a=(e,r)=>{e=o(e),r=o(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var n=e[t],a=(typeof n)[0];if(t>=r.length)return"u"==a;var i=r[t],l=(typeof i)[0];if(a!=l)return"o"==a&&"n"==l||"s"==l||"u"==a;if("o"!=a&&"u"!=a&&n!=i)return n<i;t++}},i=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,o=1;o<e.length;o++)n--,t+="u"==(typeof(l=e[o]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var a=[];for(o=1;o<e.length;o++){var l=e[o];a.push(0===l?"not("+s()+")":1===l?"("+s()+" || "+s()+")":2===l?a.pop()+" "+a.pop():i(l))}return s();function s(){return a.pop().replace(/^\((.+)\)$/,"$1")}},l=(e,r)=>{if(0 in e){r=o(r);var t=e[0],n=t<0;n&&(t=-t-1);for(var a=0,i=1,s=!0;;i++,a++){var u,f,p=i<e.length?(typeof e[i])[0]:"";if(a>=r.length||"o"==(f=(typeof(u=r[a]))[0]))return!s||("u"==p?i>t&&!n:""==p!=n);if("u"==f){if(!s||"u"!=p)return!1}else if(s)if(p==f)if(i<=t){if(u!=e[i])return!1}else{if(n?u>e[i]:u<e[i])return!1;u!=e[i]&&(s=!1)}else if("s"!=p&&"n"!=p){if(n||i<=t)return!1;s=!1,i--}else{if(i<=t||f<p!=n)return!1;s=!1}else"s"!=p&&"n"!=p&&(s=!1,i--)}}var d=[],c=d.pop.bind(d);for(a=1;a<e.length;a++){var h=e[a];d.push(1==h?c()|c():2==h?c()&c():h?l(h,r):!c())}return!!c()},s=(e,r)=>e&&S.o(e,r),u=e=>(e.loaded=1,e.get()),f=e=>Object.keys(e).reduce(((r,t)=>(e[t].eager&&(r[t]=e[t]),r)),{}),p=(e,r,t)=>{var n=t?f(e[r]):e[r];return Object.keys(n).reduce(((e,r)=>!e||!n[e].loaded&&a(e,r)?r:e),0)},d=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+i(n)+")",c=e=>{throw new Error(e)},h=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},m=(e,r,t)=>t?t():((e,r)=>c("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),v=(e=>function(r,t,n,o,a){var i=S.I(r);return i&&i.then&&!n?i.then(e.bind(e,r,S.S[r],t,!1,o,a)):e(r,S.S[r],t,n,o,a)})(((e,r,t,n,o,a)=>{if(!s(r,t))return m(e,t,a);var i=p(r,t,n);return l(o,i)||h(d(r,t,i,o)),u(r[t][i])})),g={},b={16072:()=>v("default","react",!1,[1,18],(()=>S.e(6540).then((()=>()=>S(96540))))),44209:()=>v("default","swr/immutable",!1,[1,2],(()=>S.e(6606).then((()=>()=>S(54225))))),53083:()=>v("default","rxjs",!1,[1,6],(()=>S.e(3184).then((()=>()=>S(63184))))),56339:()=>v("default","swr/infinite",!1,[1,2],(()=>S.e(5422).then((()=>()=>S(23041))))),2076:()=>v("default","react-i18next",!1,[1,16],(()=>S.e(7255).then((()=>()=>S(77255))))),92646:()=>v("default","react-dom",!1,[1,18],(()=>S.e(961).then((()=>()=>S(40961))))),57224:()=>v("default","@openmrs/esm-framework",!1,[1,9],(()=>Promise.all([S.e(6792),S.e(1997)]).then((()=>()=>S(46792)))))},y={1997:[44209,53083,56339],6072:[16072],7321:[2076,92646],8421:[57224]},w={},S.f.consumes=(e,r)=>{S.o(y,e)&&y[e].forEach((e=>{if(S.o(g,e))return r.push(g[e]);if(!w[e]){var t=r=>{g[e]=0,S.m[e]=t=>{delete S.c[e],t.exports=r()}};w[e]=!0;var n=r=>{delete g[e],S.m[e]=t=>{throw delete S.c[e],r}};try{var o=b[e]();o.then?r.push(g[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={6803:0};S.f.j=(r,t)=>{var n=S.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(1997|6072|7321)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var a=S.p+S.u(r),i=new Error;S.l(a,(t=>{if(S.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),a=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+a+")",i.name="ChunkLoadError",i.type=o,i.request=a,n[1](i)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[a,i,l]=t,s=0;if(a.some((r=>0!==e[r]))){for(n in i)S.o(i,n)&&(S.m[n]=i[n]);l&&l(S)}for(r&&r(t);s<a.length;s++)o=a[s],S.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=globalThis.webpackChunk_openmrs_esm_billing_app=globalThis.webpackChunk_openmrs_esm_billing_app||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),S.nc=void 0;var j=S(45120);_openmrs_esm_billing_app=j})();
|
|
@@ -228,7 +228,7 @@
|
|
|
228
228
|
"auxiliaryFiles": [
|
|
229
229
|
"1807.js.map"
|
|
230
230
|
],
|
|
231
|
-
"hash": "
|
|
231
|
+
"hash": "f970a301c638167b",
|
|
232
232
|
"childrenByOrder": {}
|
|
233
233
|
},
|
|
234
234
|
{
|
|
@@ -344,7 +344,7 @@
|
|
|
344
344
|
"auxiliaryFiles": [
|
|
345
345
|
"3002.js.map"
|
|
346
346
|
],
|
|
347
|
-
"hash": "
|
|
347
|
+
"hash": "3adc7bdb7d86f896",
|
|
348
348
|
"childrenByOrder": {}
|
|
349
349
|
},
|
|
350
350
|
{
|
|
@@ -371,33 +371,6 @@
|
|
|
371
371
|
"hash": "df58f1f0a7647d6a",
|
|
372
372
|
"childrenByOrder": {}
|
|
373
373
|
},
|
|
374
|
-
{
|
|
375
|
-
"rendered": true,
|
|
376
|
-
"initial": false,
|
|
377
|
-
"entry": false,
|
|
378
|
-
"recorded": false,
|
|
379
|
-
"reason": "split chunk (cache group: defaultVendors)",
|
|
380
|
-
"size": 3272580,
|
|
381
|
-
"sizes": {
|
|
382
|
-
"javascript": 3272580
|
|
383
|
-
},
|
|
384
|
-
"names": [],
|
|
385
|
-
"idHints": [
|
|
386
|
-
"vendors"
|
|
387
|
-
],
|
|
388
|
-
"runtime": [
|
|
389
|
-
"@openmrs/esm-billing-app",
|
|
390
|
-
"main"
|
|
391
|
-
],
|
|
392
|
-
"files": [
|
|
393
|
-
"3057.js"
|
|
394
|
-
],
|
|
395
|
-
"auxiliaryFiles": [
|
|
396
|
-
"3057.js.map"
|
|
397
|
-
],
|
|
398
|
-
"hash": "ea62c5c3b68346b7",
|
|
399
|
-
"childrenByOrder": {}
|
|
400
|
-
},
|
|
401
374
|
{
|
|
402
375
|
"rendered": true,
|
|
403
376
|
"initial": false,
|
|
@@ -490,7 +463,7 @@
|
|
|
490
463
|
"auxiliaryFiles": [
|
|
491
464
|
"3857.js.map"
|
|
492
465
|
],
|
|
493
|
-
"hash": "
|
|
466
|
+
"hash": "87d2ef81f6e739de",
|
|
494
467
|
"childrenByOrder": {}
|
|
495
468
|
},
|
|
496
469
|
{
|
|
@@ -566,9 +539,9 @@
|
|
|
566
539
|
"initial": false,
|
|
567
540
|
"entry": false,
|
|
568
541
|
"recorded": false,
|
|
569
|
-
"size":
|
|
542
|
+
"size": 10871,
|
|
570
543
|
"sizes": {
|
|
571
|
-
"javascript":
|
|
544
|
+
"javascript": 10871
|
|
572
545
|
},
|
|
573
546
|
"names": [],
|
|
574
547
|
"idHints": [],
|
|
@@ -580,7 +553,7 @@
|
|
|
580
553
|
"4300.js"
|
|
581
554
|
],
|
|
582
555
|
"auxiliaryFiles": [],
|
|
583
|
-
"hash": "
|
|
556
|
+
"hash": "34c3cf12c1f0b0ab",
|
|
584
557
|
"childrenByOrder": {}
|
|
585
558
|
},
|
|
586
559
|
{
|
|
@@ -964,7 +937,7 @@
|
|
|
964
937
|
"auxiliaryFiles": [
|
|
965
938
|
"6792.js.map"
|
|
966
939
|
],
|
|
967
|
-
"hash": "
|
|
940
|
+
"hash": "56b583dd855cf40e",
|
|
968
941
|
"childrenByOrder": {}
|
|
969
942
|
},
|
|
970
943
|
{
|
|
@@ -991,7 +964,7 @@
|
|
|
991
964
|
"auxiliaryFiles": [
|
|
992
965
|
"openmrs-esm-billing-app.js.map"
|
|
993
966
|
],
|
|
994
|
-
"hash": "
|
|
967
|
+
"hash": "38e030d98ce756b0",
|
|
995
968
|
"childrenByOrder": {}
|
|
996
969
|
},
|
|
997
970
|
{
|
|
@@ -1174,6 +1147,33 @@
|
|
|
1174
1147
|
"hash": "8ae03367a4bcbb11",
|
|
1175
1148
|
"childrenByOrder": {}
|
|
1176
1149
|
},
|
|
1150
|
+
{
|
|
1151
|
+
"rendered": true,
|
|
1152
|
+
"initial": false,
|
|
1153
|
+
"entry": false,
|
|
1154
|
+
"recorded": false,
|
|
1155
|
+
"reason": "split chunk (cache group: defaultVendors)",
|
|
1156
|
+
"size": 3272580,
|
|
1157
|
+
"sizes": {
|
|
1158
|
+
"javascript": 3272580
|
|
1159
|
+
},
|
|
1160
|
+
"names": [],
|
|
1161
|
+
"idHints": [
|
|
1162
|
+
"vendors"
|
|
1163
|
+
],
|
|
1164
|
+
"runtime": [
|
|
1165
|
+
"@openmrs/esm-billing-app",
|
|
1166
|
+
"main"
|
|
1167
|
+
],
|
|
1168
|
+
"files": [
|
|
1169
|
+
"8341.js"
|
|
1170
|
+
],
|
|
1171
|
+
"auxiliaryFiles": [
|
|
1172
|
+
"8341.js.map"
|
|
1173
|
+
],
|
|
1174
|
+
"hash": "46474df96f4b965f",
|
|
1175
|
+
"childrenByOrder": {}
|
|
1176
|
+
},
|
|
1177
1177
|
{
|
|
1178
1178
|
"rendered": true,
|
|
1179
1179
|
"initial": false,
|
|
@@ -1223,9 +1223,9 @@
|
|
|
1223
1223
|
"initial": false,
|
|
1224
1224
|
"entry": false,
|
|
1225
1225
|
"recorded": false,
|
|
1226
|
-
"size":
|
|
1226
|
+
"size": 1014022,
|
|
1227
1227
|
"sizes": {
|
|
1228
|
-
"javascript":
|
|
1228
|
+
"javascript": 1013980,
|
|
1229
1229
|
"consume-shared": 42
|
|
1230
1230
|
},
|
|
1231
1231
|
"names": [],
|
|
@@ -1239,7 +1239,7 @@
|
|
|
1239
1239
|
"auxiliaryFiles": [
|
|
1240
1240
|
"8421.js.map"
|
|
1241
1241
|
],
|
|
1242
|
-
"hash": "
|
|
1242
|
+
"hash": "e6912a54c96d1e98",
|
|
1243
1243
|
"childrenByOrder": {}
|
|
1244
1244
|
},
|
|
1245
1245
|
{
|
|
@@ -1269,10 +1269,10 @@
|
|
|
1269
1269
|
"initial": true,
|
|
1270
1270
|
"entry": true,
|
|
1271
1271
|
"recorded": false,
|
|
1272
|
-
"size":
|
|
1272
|
+
"size": 4660789,
|
|
1273
1273
|
"sizes": {
|
|
1274
1274
|
"consume-shared": 168,
|
|
1275
|
-
"javascript":
|
|
1275
|
+
"javascript": 4639188,
|
|
1276
1276
|
"share-init": 294,
|
|
1277
1277
|
"runtime": 21139
|
|
1278
1278
|
},
|
|
@@ -1289,7 +1289,7 @@
|
|
|
1289
1289
|
"auxiliaryFiles": [
|
|
1290
1290
|
"main.js.map"
|
|
1291
1291
|
],
|
|
1292
|
-
"hash": "
|
|
1292
|
+
"hash": "ba1fd44f34b86570",
|
|
1293
1293
|
"childrenByOrder": {}
|
|
1294
1294
|
},
|
|
1295
1295
|
{
|
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"billing":">=2.0.0","webservices.rest":">=2.24.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing history"}},{"name":"billable-services-app-menu-item","component":"billableServicesAppMenuItem","slot":"app-menu-item-slot","meta":{"name":"Billable Services"}},{"name":"billing-checkin-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"slot":"system-admin-page-card-link-slot","component":"billableServicesCardLink","name":"billable-services-admin-card-link"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"billable-services-left-panel-link","component":"billableServicesLeftPanelLink","slot":"billable-services-left-panel-slot","order":0},{"name":"billing-settings-left-panel-menu","component":"billingSettingsLeftPanelMenu","slot":"billable-services-left-panel-slot","order":1}],"modals":[{"name":"add-cash-point-modal","component":"addCashPointModal"},{"name":"payment-mode-form-modal","component":"paymentModeFormModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"edit-bill-item-modal","component":"editBillLineItemModal"},{"name":"edit-bill-line-item-modal","component":"editBillLineItemModal"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"delete-line-item-confirmation-modal","component":"deleteLineItemConfirmationModal"}],"workspaces2":[{"name":"billing-form-workspace","component":"billingFormWorkspace","window":"billing-form-window"},{"name":"billable-service-form","component":"billableServiceFormWorkspace","window":"billable-service-form-window"}],"workspaceWindows2":[{"name":"billing-form-window","group":"billingFormWorkspaceGroup"},{"name":"billable-service-form-window","group":"billingFormWorkspaceGroup","width":"wider"}],"workspaceGroups2":[{"name":"billingFormWorkspaceGroup","overlay":false}],"version":"1.1.2-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"billing":">=2.0.0","webservices.rest":">=2.24.0"},"pages":[{"component":"billableServicesHome","route":"billable-services"}],"extensions":[{"component":"billingDashboardLink","name":"billing-dashboard-link","slot":"homepage-dashboard-slot","meta":{"name":"billing","title":"billing","slot":"billing-dashboard-slot"}},{"component":"root","name":"billing-dashboard-root","slot":"billing-dashboard-slot"},{"name":"billing-patient-summary","component":"billingPatientSummary","slot":"patient-chart-billing-dashboard-slot","order":10,"meta":{"columnSpan":4}},{"name":"billing-summary-dashboard-link","component":"billingSummaryDashboardLink","slot":"patient-chart-dashboard-slot","order":11,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-billing-dashboard-slot","path":"Billing history"}},{"name":"billable-services-app-menu-item","component":"billableServicesAppMenuItem","slot":"app-menu-item-slot","meta":{"name":"Billable Services"}},{"name":"billing-checkin-form","slot":"extra-visit-attribute-slot","component":"billingCheckInForm"},{"slot":"system-admin-page-card-link-slot","component":"billableServicesCardLink","name":"billable-services-admin-card-link"},{"name":"patient-banner-billing-tags","component":"visitAttributeTags","slot":"patient-banner-tags-slot","order":2},{"name":"billable-services-left-panel-link","component":"billableServicesLeftPanelLink","slot":"billable-services-left-panel-slot","order":0},{"name":"billing-settings-left-panel-menu","component":"billingSettingsLeftPanelMenu","slot":"billable-services-left-panel-slot","order":1}],"modals":[{"name":"add-cash-point-modal","component":"addCashPointModal"},{"name":"payment-mode-form-modal","component":"paymentModeFormModal"},{"name":"delete-payment-mode-modal","component":"deletePaymentModeModal"},{"name":"edit-bill-item-modal","component":"editBillLineItemModal"},{"name":"edit-bill-line-item-modal","component":"editBillLineItemModal"},{"name":"require-billing-modal","component":"requirePaymentModal"},{"name":"delete-line-item-confirmation-modal","component":"deleteLineItemConfirmationModal"}],"workspaces2":[{"name":"billing-form-workspace","component":"billingFormWorkspace","window":"billing-form-window"},{"name":"billable-service-form","component":"billableServiceFormWorkspace","window":"billable-service-form-window"}],"workspaceWindows2":[{"name":"billing-form-window","group":"billingFormWorkspaceGroup"},{"name":"billable-service-form-window","group":"billingFormWorkspaceGroup","width":"wider"}],"workspaceGroups2":[{"name":"billingFormWorkspaceGroup","overlay":false}],"version":"1.1.2-pre.1630"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { useCallback, useState } from 'react';
|
|
2
|
-
import { Dropdown, InlineLoading, InlineNotification } from '@carbon/react';
|
|
1
|
+
import React, { useCallback, useState, useMemo, useEffect, useRef } from 'react';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { Dropdown, InlineLoading, InlineNotification } from '@carbon/react';
|
|
4
|
+
import { showSnackbar, getCoreTranslation, useConfig } from '@openmrs/esm-framework';
|
|
5
|
+
import { useCashPoint, useBillableItems, createPatientBill, useLastVisitInfo } from './billing-form.resource';
|
|
6
6
|
import VisitAttributesForm from './visit-attributes/visit-attributes-form.component';
|
|
7
7
|
import styles from './billing-checkin-form.scss';
|
|
8
8
|
|
|
@@ -15,11 +15,53 @@ type BillingCheckInFormProps = {
|
|
|
15
15
|
|
|
16
16
|
const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, setExtraVisitInfo }) => {
|
|
17
17
|
const { t } = useTranslation();
|
|
18
|
+
const { categoryConcepts } = useConfig();
|
|
19
|
+
|
|
20
|
+
const { lastVisitInfo, isLoading: isLoadingLastVisitInfo, error: lastVisitError } = useLastVisitInfo(patientUuid);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (lastVisitError) {
|
|
24
|
+
showSnackbar({
|
|
25
|
+
title: t('lastVisitError', 'Last visit error'),
|
|
26
|
+
subtitle: t('errorLoadingLastVisit', 'An error occurred while loading the last visit'),
|
|
27
|
+
kind: 'error',
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}, [lastVisitError, t]);
|
|
31
|
+
|
|
18
32
|
const { cashPoints, isLoading: isLoadingCashPoints, error: cashError } = useCashPoint();
|
|
19
33
|
const { lineItems, isLoading: isLoadingLineItems, error: lineError } = useBillableItems();
|
|
34
|
+
|
|
20
35
|
const [attributes, setAttributes] = useState([]);
|
|
21
|
-
const [paymentMethod, setPaymentMethod] = useState<
|
|
22
|
-
|
|
36
|
+
const [paymentMethod, setPaymentMethod] = useState<string | null>(null);
|
|
37
|
+
const [selectedBillableItem, setSelectedBillableItem] = useState<any | null>(null);
|
|
38
|
+
|
|
39
|
+
const attributesRef = useRef(attributes);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
attributesRef.current = attributes;
|
|
42
|
+
}, [attributes]);
|
|
43
|
+
|
|
44
|
+
const isNonPaying = useMemo(() => {
|
|
45
|
+
if (!categoryConcepts?.nonPayingDetails || !attributes?.length) return false;
|
|
46
|
+
return attributes.some((attr) => attr.value === categoryConcepts.nonPayingDetails);
|
|
47
|
+
}, [attributes, categoryConcepts]);
|
|
48
|
+
|
|
49
|
+
const lineList = useMemo(() => {
|
|
50
|
+
if (isNonPaying) return [];
|
|
51
|
+
if (!paymentMethod || !lineItems?.length) return [];
|
|
52
|
+
|
|
53
|
+
return lineItems.filter((e) => e.servicePrices.some((p) => p.paymentMode?.uuid === paymentMethod));
|
|
54
|
+
}, [lineItems, paymentMethod, isNonPaying]);
|
|
55
|
+
|
|
56
|
+
// reset bill and selection when payment changes or on switching to non-paying
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setExtraVisitInfo({
|
|
59
|
+
createBillPayload: null,
|
|
60
|
+
handleCreateExtraVisitInfo: null,
|
|
61
|
+
attributes: attributesRef.current,
|
|
62
|
+
});
|
|
63
|
+
setSelectedBillableItem(null);
|
|
64
|
+
}, [paymentMethod, isNonPaying, setExtraVisitInfo]);
|
|
23
65
|
|
|
24
66
|
const handleCreateExtraVisitInfo = useCallback(
|
|
25
67
|
async (createBillPayload) => {
|
|
@@ -41,38 +83,42 @@ const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, se
|
|
|
41
83
|
[t],
|
|
42
84
|
);
|
|
43
85
|
|
|
44
|
-
const handleBillingService = (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
// should default to first price if check returns empty. todo - update backend to return default price
|
|
49
|
-
const priceForPaymentMode =
|
|
50
|
-
selectedItem.servicePrices.find((p) => p.paymentMode?.uuid === paymentMethod) || selectedItem?.servicePrices[0];
|
|
51
|
-
|
|
52
|
-
const createBillPayload = {
|
|
53
|
-
lineItems: [
|
|
54
|
-
{
|
|
55
|
-
billableService: itemUuid,
|
|
56
|
-
quantity: 1,
|
|
57
|
-
price: priceForPaymentMode ? priceForPaymentMode.price : '0.000',
|
|
58
|
-
priceName: 'Default',
|
|
59
|
-
priceUuid: priceForPaymentMode ? priceForPaymentMode.uuid : '',
|
|
60
|
-
lineItemOrder: 0,
|
|
61
|
-
paymentStatus: PENDING_PAYMENT_STATUS,
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
cashPoint: cashPointUuid,
|
|
65
|
-
patient: patientUuid,
|
|
66
|
-
status: PENDING_PAYMENT_STATUS,
|
|
67
|
-
payments: [],
|
|
68
|
-
};
|
|
86
|
+
const handleBillingService = useCallback(
|
|
87
|
+
({ selectedItem }: { selectedItem }) => {
|
|
88
|
+
setSelectedBillableItem(selectedItem);
|
|
69
89
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
90
|
+
const cashPointUuid = cashPoints?.[0]?.uuid ?? '';
|
|
91
|
+
const itemUuid = selectedItem?.uuid ?? '';
|
|
92
|
+
|
|
93
|
+
// should default to first price if check returns empty. todo - update backend to return default price
|
|
94
|
+
const priceForPaymentMode =
|
|
95
|
+
selectedItem.servicePrices.find((p) => p.paymentMode?.uuid === paymentMethod) || selectedItem?.servicePrices[0];
|
|
96
|
+
|
|
97
|
+
const createBillPayload = {
|
|
98
|
+
lineItems: [
|
|
99
|
+
{
|
|
100
|
+
billableService: itemUuid,
|
|
101
|
+
quantity: 1,
|
|
102
|
+
price: priceForPaymentMode ? priceForPaymentMode.price : '0.000',
|
|
103
|
+
priceName: 'Default',
|
|
104
|
+
priceUuid: priceForPaymentMode ? priceForPaymentMode.uuid : '',
|
|
105
|
+
lineItemOrder: 0,
|
|
106
|
+
paymentStatus: PENDING_PAYMENT_STATUS,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
cashPoint: cashPointUuid,
|
|
110
|
+
patient: patientUuid,
|
|
111
|
+
status: PENDING_PAYMENT_STATUS,
|
|
112
|
+
payments: [],
|
|
113
|
+
};
|
|
114
|
+
setExtraVisitInfo({
|
|
115
|
+
createBillPayload,
|
|
116
|
+
handleCreateExtraVisitInfo: () => handleCreateExtraVisitInfo(createBillPayload),
|
|
117
|
+
attributes,
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
[attributes, cashPoints, handleCreateExtraVisitInfo, paymentMethod, patientUuid, setExtraVisitInfo],
|
|
121
|
+
);
|
|
76
122
|
|
|
77
123
|
if (isLoadingLineItems || isLoadingCashPoints) {
|
|
78
124
|
return (
|
|
@@ -84,13 +130,6 @@ const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, se
|
|
|
84
130
|
);
|
|
85
131
|
}
|
|
86
132
|
|
|
87
|
-
if (paymentMethod) {
|
|
88
|
-
lineList = [];
|
|
89
|
-
lineList = lineItems.filter((e) =>
|
|
90
|
-
e.servicePrices.some((p) => p.paymentMode && p.paymentMode.uuid === paymentMethod),
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
133
|
const setServicePrice = (prices) => {
|
|
95
134
|
const matchingPrice = prices.find((p) => p.paymentMode?.uuid === paymentMethod);
|
|
96
135
|
return matchingPrice ? `(${matchingPrice.name}: ${matchingPrice.price})` : '';
|
|
@@ -109,17 +148,38 @@ const BillingCheckInForm: React.FC<BillingCheckInFormProps> = ({ patientUuid, se
|
|
|
109
148
|
|
|
110
149
|
return (
|
|
111
150
|
<section className={styles.sectionContainer}>
|
|
112
|
-
<
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
151
|
+
<h1 className={styles.sectionLabel}>{t('billingDetails', 'Billing details')}</h1>
|
|
152
|
+
<div className={styles.sectionField}>
|
|
153
|
+
{!isLoadingLastVisitInfo && !lastVisitError && lastVisitInfo && (
|
|
154
|
+
<div className={styles.lastVisitBanner}>
|
|
155
|
+
<InlineNotification
|
|
156
|
+
hideCloseButton
|
|
157
|
+
kind="info"
|
|
158
|
+
title={t('lastVisitInfo', 'Last Visit Information')}
|
|
159
|
+
subtitle={t('lastVisitMsg', 'The last visit was a {{type}} {{count}} days ago at {{location}}', {
|
|
160
|
+
count: lastVisitInfo.diffDays,
|
|
161
|
+
type: lastVisitInfo.type,
|
|
162
|
+
location: lastVisitInfo.location,
|
|
163
|
+
})}
|
|
164
|
+
lowContrast
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
<VisitAttributesForm setAttributes={setAttributes} setPaymentMethod={setPaymentMethod} />
|
|
169
|
+
|
|
170
|
+
{lineList.length > 0 && (
|
|
171
|
+
<Dropdown
|
|
172
|
+
key={`billable-${paymentMethod}`}
|
|
173
|
+
id="billable-items"
|
|
174
|
+
items={lineList}
|
|
175
|
+
itemToString={(item) => (item ? `${item.name} ${setServicePrice(item.servicePrices)}` : '')}
|
|
176
|
+
label={t('selectBillableService', 'Select a billable service')}
|
|
177
|
+
onChange={handleBillingService}
|
|
178
|
+
selectedItem={selectedBillableItem}
|
|
179
|
+
titleText={t('billableService', 'Billable service')}
|
|
180
|
+
/>
|
|
181
|
+
)}
|
|
182
|
+
</div>
|
|
123
183
|
</section>
|
|
124
184
|
);
|
|
125
185
|
};
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@carbon/colors';
|
|
2
4
|
|
|
3
|
-
.
|
|
4
|
-
|
|
5
|
+
.sectionLabel {
|
|
6
|
+
@include type.type-style('heading-compact-02');
|
|
7
|
+
color: colors.$gray-70;
|
|
8
|
+
margin: 0 0 layout.$spacing-03 0;
|
|
9
|
+
min-width: 8rem;
|
|
5
10
|
}
|
|
6
11
|
|
|
12
|
+
.lastVisitBanner {
|
|
13
|
+
margin-bottom: layout.$spacing-05;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:global(.omrs-breakpoint-lt-desktop) {
|
|
17
|
+
.sectionContainer {
|
|
18
|
+
display: flex;
|
|
19
|
+
|
|
20
|
+
.sectionLabel {
|
|
21
|
+
flex-basis: 30%;
|
|
22
|
+
min-width: 8rem;
|
|
23
|
+
text-align: left;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.sectionField {
|
|
27
|
+
flex-basis: 70%;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -3,13 +3,14 @@ import userEvent from '@testing-library/user-event';
|
|
|
3
3
|
import { screen, render } from '@testing-library/react';
|
|
4
4
|
import { useConfig } from '@openmrs/esm-framework';
|
|
5
5
|
import { type BillingConfig } from '../config-schema';
|
|
6
|
-
import { useBillableItems, useCashPoint, usePaymentMethods } from './billing-form.resource';
|
|
6
|
+
import { useBillableItems, useCashPoint, usePaymentMethods, useLastVisitInfo } from './billing-form.resource';
|
|
7
7
|
import BillingCheckInForm from './billing-checkin-form.component';
|
|
8
8
|
|
|
9
9
|
const mockUseConfig = jest.mocked(useConfig<BillingConfig>);
|
|
10
10
|
const mockUseCashPoint = jest.mocked(useCashPoint);
|
|
11
11
|
const mockUseBillableItems = jest.mocked(useBillableItems);
|
|
12
12
|
const mockUsePaymentMethods = jest.mocked(usePaymentMethods);
|
|
13
|
+
const mockUseLastVisitInfo = jest.mocked(useLastVisitInfo);
|
|
13
14
|
|
|
14
15
|
const mockCashPoints = [
|
|
15
16
|
{
|
|
@@ -89,6 +90,7 @@ jest.mock('./billing-form.resource', () => ({
|
|
|
89
90
|
useCashPoint: jest.fn(),
|
|
90
91
|
createPatientBill: jest.fn(),
|
|
91
92
|
usePaymentMethods: jest.fn(),
|
|
93
|
+
useLastVisitInfo: jest.fn(),
|
|
92
94
|
}));
|
|
93
95
|
|
|
94
96
|
const testProps = { patientUuid: 'some-patient-uuid', setExtraVisitInfo: jest.fn() };
|
|
@@ -116,6 +118,7 @@ describe('BillingCheckInForm', () => {
|
|
|
116
118
|
},
|
|
117
119
|
} as BillingConfig);
|
|
118
120
|
mockUsePaymentMethods.mockReturnValue({ paymentModes: mockPaymentMethods, isLoading: false, error: null });
|
|
121
|
+
mockUseLastVisitInfo.mockReturnValue({ lastVisitInfo: null, isLoading: false, error: null });
|
|
119
122
|
});
|
|
120
123
|
|
|
121
124
|
test('should show the loading spinner while retrieving data', () => {
|
|
@@ -136,6 +139,53 @@ describe('BillingCheckInForm', () => {
|
|
|
136
139
|
expect(screen.getByText(/error loading bill services/i)).toBeInTheDocument();
|
|
137
140
|
});
|
|
138
141
|
|
|
142
|
+
test('should show the last visit banner when last visit info is available', () => {
|
|
143
|
+
mockUseBillableItems.mockReturnValue({ lineItems: [], isLoading: false, error: null });
|
|
144
|
+
mockUseCashPoint.mockReturnValue({ cashPoints: [], isLoading: false, error: null });
|
|
145
|
+
mockUseLastVisitInfo.mockReturnValue({
|
|
146
|
+
lastVisitInfo: { diffDays: 3, type: 'Outpatient', location: 'Main Clinic' },
|
|
147
|
+
isLoading: false,
|
|
148
|
+
error: null,
|
|
149
|
+
});
|
|
150
|
+
renderBillingCheckinForm();
|
|
151
|
+
|
|
152
|
+
expect(screen.getByText(/Last Visit Information/i)).toBeInTheDocument();
|
|
153
|
+
expect(screen.getByText(/3 days ago/i)).toBeInTheDocument();
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('should not show the last visit banner when there is no recent visit', () => {
|
|
157
|
+
mockUseBillableItems.mockReturnValue({ lineItems: [], isLoading: false, error: null });
|
|
158
|
+
mockUseCashPoint.mockReturnValue({ cashPoints: [], isLoading: false, error: null });
|
|
159
|
+
mockUseLastVisitInfo.mockReturnValue({ lastVisitInfo: null, isLoading: false, error: null });
|
|
160
|
+
renderBillingCheckinForm();
|
|
161
|
+
|
|
162
|
+
expect(screen.queryByText(/Last Visit Information/i)).not.toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('should show billable service dropdown when a paying method is selected', async () => {
|
|
166
|
+
const user = userEvent.setup();
|
|
167
|
+
mockUseCashPoint.mockReturnValue({ cashPoints: mockCashPoints, isLoading: false, error: null });
|
|
168
|
+
mockUseBillableItems.mockReturnValue({ lineItems: mockBillableItems, isLoading: false, error: null });
|
|
169
|
+
renderBillingCheckinForm();
|
|
170
|
+
|
|
171
|
+
await user.click(screen.getByRole('radio', { name: 'Paying' }));
|
|
172
|
+
const paymentMethodDropdown = await screen.findByRole('combobox', { name: /payment method/i });
|
|
173
|
+
await user.click(paymentMethodDropdown);
|
|
174
|
+
await user.click(await screen.findByText('Insurance'));
|
|
175
|
+
|
|
176
|
+
expect(screen.getByRole('combobox', { name: /billable service/i })).toBeInTheDocument();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('should hide billable service dropdown when switched to non-paying', async () => {
|
|
180
|
+
const user = userEvent.setup();
|
|
181
|
+
mockUseCashPoint.mockReturnValue({ cashPoints: mockCashPoints, isLoading: false, error: null });
|
|
182
|
+
mockUseBillableItems.mockReturnValue({ lineItems: mockBillableItems, isLoading: false, error: null });
|
|
183
|
+
renderBillingCheckinForm();
|
|
184
|
+
|
|
185
|
+
await user.click(screen.getByRole('radio', { name: /non paying/i }));
|
|
186
|
+
expect(screen.queryByRole('combobox', { name: /billable service/i })).not.toBeInTheDocument();
|
|
187
|
+
});
|
|
188
|
+
|
|
139
189
|
test('should render the form correctly and generate the required payload', async () => {
|
|
140
190
|
const user = userEvent.setup();
|
|
141
191
|
mockUseCashPoint.mockReturnValue({ cashPoints: mockCashPoints, isLoading: false, error: null });
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { renderHook } from '@testing-library/react';
|
|
2
|
+
import useSWR from 'swr';
|
|
3
|
+
import { useLastVisitInfo } from './billing-form.resource';
|
|
4
|
+
|
|
5
|
+
jest.mock('swr');
|
|
6
|
+
const mockUseSWR = jest.mocked(useSWR);
|
|
7
|
+
|
|
8
|
+
// Pin "now" so diffDays calculations are deterministic
|
|
9
|
+
const FIXED_NOW = new Date('2026-01-10T12:00:00Z').getTime();
|
|
10
|
+
|
|
11
|
+
describe('useLastVisitInfo', () => {
|
|
12
|
+
beforeAll(() => {
|
|
13
|
+
jest.useFakeTimers();
|
|
14
|
+
jest.setSystemTime(FIXED_NOW);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterAll(() => {
|
|
18
|
+
jest.useRealTimers();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns null lastVisitInfo when there is no visit data yet', () => {
|
|
22
|
+
mockUseSWR.mockReturnValue({ data: undefined, isLoading: false, error: undefined } as any);
|
|
23
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
24
|
+
expect(result.current.lastVisitInfo).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns null lastVisitInfo when the visits list is empty', () => {
|
|
28
|
+
mockUseSWR.mockReturnValue({ data: { data: { results: [] } }, isLoading: false, error: undefined } as any);
|
|
29
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
30
|
+
expect(result.current.lastVisitInfo).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('forwards isLoading and error from SWR', () => {
|
|
34
|
+
const error = new Error('Network error');
|
|
35
|
+
mockUseSWR.mockReturnValue({ data: undefined, isLoading: true, error } as any);
|
|
36
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
37
|
+
expect(result.current.isLoading).toBe(true);
|
|
38
|
+
expect(result.current.error).toBe(error);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('returns correct visit info for a visit 2 days ago', () => {
|
|
42
|
+
const twoDaysAgo = new Date(FIXED_NOW - 2 * 24 * 60 * 60 * 1000).toISOString();
|
|
43
|
+
mockUseSWR.mockReturnValue({
|
|
44
|
+
data: {
|
|
45
|
+
data: {
|
|
46
|
+
results: [
|
|
47
|
+
{
|
|
48
|
+
startDatetime: twoDaysAgo,
|
|
49
|
+
visitType: { display: 'Outpatient' },
|
|
50
|
+
location: { display: 'Main Clinic' },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
isLoading: false,
|
|
56
|
+
error: undefined,
|
|
57
|
+
} as any);
|
|
58
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
59
|
+
expect(result.current.lastVisitInfo).toEqual({ diffDays: 2, type: 'Outpatient', location: 'Main Clinic' });
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('returns diffDays of 1 for a visit exactly 1 day ago', () => {
|
|
63
|
+
const oneDayAgo = new Date(FIXED_NOW - 1 * 24 * 60 * 60 * 1000).toISOString();
|
|
64
|
+
mockUseSWR.mockReturnValue({
|
|
65
|
+
data: {
|
|
66
|
+
data: {
|
|
67
|
+
results: [{ startDatetime: oneDayAgo, visitType: { display: 'Inpatient' }, location: { display: 'Ward A' } }],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
isLoading: false,
|
|
71
|
+
error: undefined,
|
|
72
|
+
} as any);
|
|
73
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
74
|
+
expect(result.current.lastVisitInfo?.diffDays).toBe(1);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('falls back to empty strings when visitType and location are absent', () => {
|
|
78
|
+
const oneDayAgo = new Date(FIXED_NOW - 1 * 24 * 60 * 60 * 1000).toISOString();
|
|
79
|
+
mockUseSWR.mockReturnValue({
|
|
80
|
+
data: { data: { results: [{ startDatetime: oneDayAgo, visitType: null, location: null }] } },
|
|
81
|
+
isLoading: false,
|
|
82
|
+
error: undefined,
|
|
83
|
+
} as any);
|
|
84
|
+
const { result } = renderHook(() => useLastVisitInfo('patient-uuid'));
|
|
85
|
+
expect(result.current.lastVisitInfo).toEqual({ diffDays: 1, type: '', location: '' });
|
|
86
|
+
});
|
|
87
|
+
});
|