@openmrs/esm-fast-data-entry-app 1.0.0-pre.51 → 1.0.0-pre.57

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.
@@ -1 +1 @@
1
- System.register("@openmrs/esm-fast-data-entry-app",[],(function(e,r){return{execute:function(){e((()=>{var e,t,n,o,a,i,s,u,l,f,d,p,c,h,m={1858:(e,r,t)=>{(0,t(2722).s)(1)},2722:(e,r,t)=>{const n=t(3905).R;r.s=function(e){if(e||(e=1),!t.y.meta||!t.y.meta.url)throw console.error("__system_context__",t.y),Error("systemjs-webpack-interop was provided an unknown SystemJS context. Expected context.meta.url, but none was provided");t.p=n(t.y.meta.url,e)}},5356:(e,r,t)=>{t(1858)},3905:(e,r,t)=>{r.R=function(e,r){var t=document.createElement("a");t.href=e;for(var n="/"===t.pathname[0]?t.pathname:"/"+t.pathname,o=0,a=n.length;o!==r&&a>=0;)"/"===n[--a]&&o++;if(o!==r)throw Error("systemjs-webpack-interop: rootDirectoryLevel ("+r+") is greater than the number of directories ("+o+") in the URL path "+e);var i=n.slice(0,a+1);return t.protocol+"//"+t.host+i};Number.isInteger},6751:(e,r,t)=>{"use strict";var n={app:()=>t.e(132).then((()=>()=>t(8132)))},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})}},v={};function y(e){var r=v[e];if(void 0!==r)return r.exports;var t=v[e]={id:e,loaded:!1,exports:{}};return m[e].call(t.exports,t,t.exports,y),t.loaded=!0,t.exports}return y.m=m,y.c=v,y.y=r,y.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return y.d(r,{a:r}),r},y.d=(e,r)=>{for(var t in r)y.o(r,t)&&!y.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},y.f={},y.e=e=>Promise.all(Object.keys(y.f).reduce(((r,t)=>(y.f[t](e,r),r)),[])),y.u=e=>e+".js",y.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),y.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},t="@openmrs/esm-fast-data-entry-app:",y.l=(r,n,o,a)=>{if(e[r])e[r].push(n);else{var i,s;if(void 0!==o)for(var u=document.getElementsByTagName("script"),l=0;l<u.length;l++){var f=u[l];if(f.getAttribute("src")==r||f.getAttribute("data-webpack")==t+o){i=f;break}}i||(s=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,y.nc&&i.setAttribute("nonce",y.nc),i.setAttribute("data-webpack",t+o),i.src=r),e[r]=[n];var d=(t,n)=>{i.onerror=i.onload=null,clearTimeout(p);var o=e[r];if(delete e[r],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(n))),t)return t(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),s&&document.head.appendChild(i)}},y.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},y.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{y.S={};var e={},r={};y.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];y.o(y.S,t)||(y.S[t]={});var a=y.S[t],i="@openmrs/esm-fast-data-entry-app",s=(e,r,t,n)=>{var o=a[e]=a[e]||{},s=o[r];(!s||!s.loaded&&(!n!=!s.eager?n:i>s.from))&&(o[r]={get:t,from:i,eager:!!n})},u=[];return"default"===t&&(s("@openmrs/esm-framework","3.4.1-pre.161",(()=>Promise.all([y.e(595),y.e(211)]).then((()=>()=>y(595))))),s("carbon-icons","7.0.7",(()=>y.e(906).then((()=>()=>y(906))))),s("react-dom","16.14.0",(()=>Promise.all([y.e(935),y.e(211)]).then((()=>()=>y(3935))))),s("react-i18next","11.18.1",(()=>Promise.all([y.e(247),y.e(211)]).then((()=>()=>y(8247))))),s("react-router-dom","5.3.3",(()=>Promise.all([y.e(536),y.e(211)]).then((()=>()=>y(536))))),s("react","16.14.0",(()=>y.e(294).then((()=>()=>y(7294)))))),e[t]=u.length?Promise.all(u).then((()=>e[t]=1)):1}}})(),y.p="",n=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},o=(e,r)=>{e=n(e),r=n(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var o=e[t],a=(typeof o)[0];if(t>=r.length)return"u"==a;var i=r[t],s=(typeof i)[0];if(a!=s)return"o"==a&&"n"==s||"s"==s||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;t++}},a=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(s=e[o]))[0]?"-":(n>0?".":"")+(n=2,s);return t}var i=[];for(o=1;o<e.length;o++){var s=e[o];i.push(0===s?"not("+u()+")":1===s?"("+u()+" || "+u()+")":2===s?i.pop()+" "+i.pop():a(s))}return u();function u(){return i.pop().replace(/^\((.+)\)$/,"$1")}},i=(e,r)=>{if(0 in e){r=n(r);var t=e[0],o=t<0;o&&(t=-t-1);for(var a=0,s=1,u=!0;;s++,a++){var l,f,d=s<e.length?(typeof e[s])[0]:"";if(a>=r.length||"o"==(f=(typeof(l=r[a]))[0]))return!u||("u"==d?s>t&&!o:""==d!=o);if("u"==f){if(!u||"u"!=d)return!1}else if(u)if(d==f)if(s<=t){if(l!=e[s])return!1}else{if(o?l>e[s]:l<e[s])return!1;l!=e[s]&&(u=!1)}else if("s"!=d&&"n"!=d){if(o||s<=t)return!1;u=!1,s--}else{if(s<=t||f<d!=o)return!1;u=!1}else"s"!=d&&"n"!=d&&(u=!1,s--)}}var p=[],c=p.pop.bind(p);for(a=1;a<e.length;a++){var h=e[a];p.push(1==h?c()|c():2==h?c()&c():h?i(h,r):!c())}return!!c()},s=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&o(e,r)?r:e),0)},u=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",l=(e,r,t,n)=>{var o=s(e,t);return i(n,o)||"undefined"!=typeof console&&console.warn&&console.warn(u(e,t,o,n)),f(e[t][o])},f=e=>(e.loaded=1,e.get()),d=(e=>function(r,t,n,o){var a=y.I(r);return a&&a.then?a.then(e.bind(e,r,y.S[r],t,n,o)):e(0,y.S[r],t,n,o)})(((e,r,t,n,o)=>r&&y.o(r,t)?l(r,0,t,n):o())),p={},c={311:()=>d("default","@openmrs/esm-framework",[1,"next"],(()=>Promise.all([y.e(595),y.e(211)]).then((()=>()=>y(595))))),4211:()=>d("default","react",[1,16,14],(()=>y.e(294).then((()=>()=>y(7294))))),2221:()=>d("default","react-router-dom",[1,5,0,0],(()=>y.e(536).then((()=>()=>y(536))))),3397:()=>d("default","react-i18next",[1,11],(()=>y.e(247).then((()=>()=>y(8247))))),8408:()=>d("default","react-dom",[1,16,0],(()=>y.e(935).then((()=>()=>y(3935)))))},h={132:[311],211:[4211],397:[3397],804:[2221],990:[8408]},y.f.consumes=(e,r)=>{y.o(h,e)&&h[e].forEach((e=>{if(y.o(p,e))return r.push(p[e]);var t=r=>{p[e]=0,y.m[e]=t=>{delete y.c[e],t.exports=r()}},n=r=>{delete p[e],y.m[e]=t=>{throw delete y.c[e],r}};try{var o=c[e]();o.then?r.push(p[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}))},(()=>{var e={447:0};y.f.j=(r,t)=>{var n=y.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(211|397)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var a=y.p+y.u(r),i=new Error;y.l(a,(t=>{if(y.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,s]=t,u=0;if(a.some((r=>0!==e[r]))){for(n in i)y.o(i,n)&&(y.m[n]=i[n]);s&&s(y)}for(r&&r(t);u<a.length;u++)o=a[u],y.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunk_openmrs_esm_fast_data_entry_app=self.webpackChunk_openmrs_esm_fast_data_entry_app||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),y.nc=void 0,y(5356),y(6751)})())}}}));
1
+ System.register("@openmrs/esm-fast-data-entry-app",[],(function(e,r){return{execute:function(){e((()=>{var e,t,n,o,a,i,s,u,l,f,d,p,c,h,m={1858:(e,r,t)=>{(0,t(2722).s)(1)},2722:(e,r,t)=>{const n=t(3905).R;r.s=function(e){if(e||(e=1),!t.y.meta||!t.y.meta.url)throw console.error("__system_context__",t.y),Error("systemjs-webpack-interop was provided an unknown SystemJS context. Expected context.meta.url, but none was provided");t.p=n(t.y.meta.url,e)}},5356:(e,r,t)=>{t(1858)},3905:(e,r,t)=>{r.R=function(e,r){var t=document.createElement("a");t.href=e;for(var n="/"===t.pathname[0]?t.pathname:"/"+t.pathname,o=0,a=n.length;o!==r&&a>=0;)"/"===n[--a]&&o++;if(o!==r)throw Error("systemjs-webpack-interop: rootDirectoryLevel ("+r+") is greater than the number of directories ("+o+") in the URL path "+e);var i=n.slice(0,a+1);return t.protocol+"//"+t.host+i};Number.isInteger},6751:(e,r,t)=>{"use strict";var n={app:()=>t.e(132).then((()=>()=>t(8132)))},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})}},v={};function y(e){var r=v[e];if(void 0!==r)return r.exports;var t=v[e]={id:e,loaded:!1,exports:{}};return m[e].call(t.exports,t,t.exports,y),t.loaded=!0,t.exports}return y.m=m,y.c=v,y.y=r,y.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return y.d(r,{a:r}),r},y.d=(e,r)=>{for(var t in r)y.o(r,t)&&!y.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},y.f={},y.e=e=>Promise.all(Object.keys(y.f).reduce(((r,t)=>(y.f[t](e,r),r)),[])),y.u=e=>e+".js",y.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),y.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),e={},t="@openmrs/esm-fast-data-entry-app:",y.l=(r,n,o,a)=>{if(e[r])e[r].push(n);else{var i,s;if(void 0!==o)for(var u=document.getElementsByTagName("script"),l=0;l<u.length;l++){var f=u[l];if(f.getAttribute("src")==r||f.getAttribute("data-webpack")==t+o){i=f;break}}i||(s=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,y.nc&&i.setAttribute("nonce",y.nc),i.setAttribute("data-webpack",t+o),i.src=r),e[r]=[n];var d=(t,n)=>{i.onerror=i.onload=null,clearTimeout(p);var o=e[r];if(delete e[r],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach((e=>e(n))),t)return t(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=d.bind(null,i.onerror),i.onload=d.bind(null,i.onload),s&&document.head.appendChild(i)}},y.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},y.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{y.S={};var e={},r={};y.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];y.o(y.S,t)||(y.S[t]={});var a=y.S[t],i="@openmrs/esm-fast-data-entry-app",s=(e,r,t,n)=>{var o=a[e]=a[e]||{},s=o[r];(!s||!s.loaded&&(!n!=!s.eager?n:i>s.from))&&(o[r]={get:t,from:i,eager:!!n})},u=[];return"default"===t&&(s("@openmrs/esm-framework","3.4.1-pre.180",(()=>Promise.all([y.e(595),y.e(211)]).then((()=>()=>y(595))))),s("carbon-icons","7.0.7",(()=>y.e(906).then((()=>()=>y(906))))),s("react-dom","16.14.0",(()=>Promise.all([y.e(935),y.e(211)]).then((()=>()=>y(3935))))),s("react-i18next","11.18.3",(()=>Promise.all([y.e(247),y.e(211)]).then((()=>()=>y(8247))))),s("react-router-dom","5.3.3",(()=>Promise.all([y.e(536),y.e(211)]).then((()=>()=>y(536))))),s("react","16.14.0",(()=>y.e(294).then((()=>()=>y(7294)))))),e[t]=u.length?Promise.all(u).then((()=>e[t]=1)):1}}})(),y.p="",n=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},o=(e,r)=>{e=n(e),r=n(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var o=e[t],a=(typeof o)[0];if(t>=r.length)return"u"==a;var i=r[t],s=(typeof i)[0];if(a!=s)return"o"==a&&"n"==s||"s"==s||"u"==a;if("o"!=a&&"u"!=a&&o!=i)return o<i;t++}},a=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(s=e[o]))[0]?"-":(n>0?".":"")+(n=2,s);return t}var i=[];for(o=1;o<e.length;o++){var s=e[o];i.push(0===s?"not("+u()+")":1===s?"("+u()+" || "+u()+")":2===s?i.pop()+" "+i.pop():a(s))}return u();function u(){return i.pop().replace(/^\((.+)\)$/,"$1")}},i=(e,r)=>{if(0 in e){r=n(r);var t=e[0],o=t<0;o&&(t=-t-1);for(var a=0,s=1,u=!0;;s++,a++){var l,f,d=s<e.length?(typeof e[s])[0]:"";if(a>=r.length||"o"==(f=(typeof(l=r[a]))[0]))return!u||("u"==d?s>t&&!o:""==d!=o);if("u"==f){if(!u||"u"!=d)return!1}else if(u)if(d==f)if(s<=t){if(l!=e[s])return!1}else{if(o?l>e[s]:l<e[s])return!1;l!=e[s]&&(u=!1)}else if("s"!=d&&"n"!=d){if(o||s<=t)return!1;u=!1,s--}else{if(s<=t||f<d!=o)return!1;u=!1}else"s"!=d&&"n"!=d&&(u=!1,s--)}}var p=[],c=p.pop.bind(p);for(a=1;a<e.length;a++){var h=e[a];p.push(1==h?c()|c():2==h?c()&c():h?i(h,r):!c())}return!!c()},s=(e,r)=>{var t=e[r];return Object.keys(t).reduce(((e,r)=>!e||!t[e].loaded&&o(e,r)?r:e),0)},u=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",l=(e,r,t,n)=>{var o=s(e,t);return i(n,o)||"undefined"!=typeof console&&console.warn&&console.warn(u(e,t,o,n)),f(e[t][o])},f=e=>(e.loaded=1,e.get()),d=(e=>function(r,t,n,o){var a=y.I(r);return a&&a.then?a.then(e.bind(e,r,y.S[r],t,n,o)):e(0,y.S[r],t,n,o)})(((e,r,t,n,o)=>r&&y.o(r,t)?l(r,0,t,n):o())),p={},c={311:()=>d("default","@openmrs/esm-framework",[1,"next"],(()=>Promise.all([y.e(595),y.e(211)]).then((()=>()=>y(595))))),4211:()=>d("default","react",[1,16,14],(()=>y.e(294).then((()=>()=>y(7294))))),2221:()=>d("default","react-router-dom",[1,5,0,0],(()=>y.e(536).then((()=>()=>y(536))))),3397:()=>d("default","react-i18next",[1,11],(()=>y.e(247).then((()=>()=>y(8247))))),8408:()=>d("default","react-dom",[1,16,0],(()=>y.e(935).then((()=>()=>y(3935)))))},h={132:[311],211:[4211],397:[3397],804:[2221],990:[8408]},y.f.consumes=(e,r)=>{y.o(h,e)&&h[e].forEach((e=>{if(y.o(p,e))return r.push(p[e]);var t=r=>{p[e]=0,y.m[e]=t=>{delete y.c[e],t.exports=r()}},n=r=>{delete p[e],y.m[e]=t=>{throw delete y.c[e],r}};try{var o=c[e]();o.then?r.push(p[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}))},(()=>{var e={447:0};y.f.j=(r,t)=>{var n=y.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(211|397)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var a=y.p+y.u(r),i=new Error;y.l(a,(t=>{if(y.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,s]=t,u=0;if(a.some((r=>0!==e[r]))){for(n in i)y.o(i,n)&&(y.m[n]=i[n]);s&&s(y)}for(r&&r(t);u<a.length;u++)o=a[u],y.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=self.webpackChunk_openmrs_esm_fast_data_entry_app=self.webpackChunk_openmrs_esm_fast_data_entry_app||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),y.nc=void 0,y(5356),y(6751)})())}}}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-fast-data-entry-app",
3
- "version": "1.0.0-pre.51",
3
+ "version": "1.0.0-pre.57",
4
4
  "license": "MPL-2.0",
5
5
  "description": "An OpenMRS 3.x microfrontend",
6
6
  "browser": "dist/openmrs-esm-fast-data-entry-app.js",
@@ -1,17 +1,11 @@
1
1
  import React, { useEffect, useMemo, useReducer } from "react";
2
2
  import reducer from "./FormWorkflowReducer";
3
- import { useParams } from "react-router-dom";
3
+ import { useParams, useLocation } from "react-router-dom";
4
4
  interface ParamTypes {
5
5
  formUuid: string;
6
6
  }
7
7
 
8
- const initialState = {
9
- formUuid: null,
10
- patientUuids: [],
11
- activePatientUuid: null,
12
- activeEncounterUuid: null,
13
- encounters: {},
14
- workflowState: null,
8
+ const initialActions = {
15
9
  addPatient: (uuid: string | number) => undefined,
16
10
  openPatientSearch: () => undefined,
17
11
  saveEncounter: (encounterUuid: string | number) => undefined,
@@ -21,14 +15,43 @@ const initialState = {
21
15
  goToReview: () => undefined,
22
16
  };
23
17
 
24
- const FormWorkflowContext = React.createContext(initialState);
18
+ export const initialWorkflowState = {
19
+ // activeFormUuid and forms are the only two real values stored at state root level
20
+ activeFormUuid: null, // the corrently open form
21
+ forms: {}, // object containing all forms session data
22
+ // the following fields will be available in context but are not stored at the
23
+ // state root level, but refer to nested values for the current
24
+ // aciveFormUuid
25
+ workflowState: null, // pseudo field from state[activeFormUuid].workflowState
26
+ activePatientUuid: null, // pseudo field from state[activeFormUuid].activePatientUuid
27
+ activeEncounterUuid: null, // pseudo field from state[activeFormUuid].encounterUuid
28
+ patientUuids: [], // pseudo field from state[activeFormUuid].patientUuids
29
+ encounters: {}, // pseudo field from state[activeFormUuid].encounters
30
+ };
31
+
32
+ const FormWorkflowContext = React.createContext({
33
+ ...initialWorkflowState,
34
+ ...initialActions,
35
+ });
25
36
 
26
37
  const FormWorkflowProvider = ({ children }) => {
27
- const { formUuid: paramFormUuid } = useParams() as ParamTypes;
28
- const [state, dispatch] = useReducer(reducer, { ...initialState });
38
+ const { formUuid } = useParams() as ParamTypes;
39
+ const activeFormUuid = formUuid.split("&")[0];
40
+ const { search } = useLocation();
41
+ const newPatientUuid = new URLSearchParams(search).get("patientUuid");
42
+ const [state, dispatch] = useReducer(reducer, {
43
+ ...initialWorkflowState,
44
+ ...initialActions,
45
+ });
29
46
 
30
47
  const actions = useMemo(
31
48
  () => ({
49
+ initializeWorkflowState: ({ activeFormUuid, newPatientUuid }) =>
50
+ dispatch({
51
+ type: "INITIALIZE_WORKFLOW_STATE",
52
+ activeFormUuid,
53
+ newPatientUuid,
54
+ }),
32
55
  addPatient: (patientUuid) =>
33
56
  dispatch({ type: "ADD_PATIENT", patientUuid }),
34
57
  openPatientSearch: () => dispatch({ type: "OPEN_PATIENT_SEARCH" }),
@@ -47,18 +70,35 @@ const FormWorkflowProvider = ({ children }) => {
47
70
  );
48
71
 
49
72
  // if formUuid isn't a part of state yet, grab it from the url params
73
+ // this is the entry into the workflow system
50
74
  useEffect(() => {
51
- if (!state.formUuid && paramFormUuid) {
52
- dispatch({ type: "UPDATE_FORM_UUID", formUuid: paramFormUuid });
75
+ if (state?.workflowState === null && activeFormUuid) {
76
+ actions.initializeWorkflowState({ activeFormUuid, newPatientUuid });
53
77
  }
54
- }, [paramFormUuid, state.formUuid]);
55
-
56
- useEffect(() => {
57
- actions.openPatientSearch();
58
- }, [actions]);
78
+ }, [activeFormUuid, newPatientUuid, state?.workflowState, actions]);
59
79
 
60
80
  return (
61
- <FormWorkflowContext.Provider value={{ ...state, ...actions }}>
81
+ <FormWorkflowContext.Provider
82
+ value={{
83
+ ...state,
84
+ ...actions,
85
+ workflowState:
86
+ state.forms?.[state.activeFormUuid]?.workflowState ??
87
+ initialWorkflowState.workflowState,
88
+ activePatientUuid:
89
+ state.forms?.[state.activeFormUuid]?.activePatientUuid ??
90
+ initialWorkflowState.activePatientUuid,
91
+ activeEncounterUuid:
92
+ state.forms?.[state.activeFormUuid]?.activeEncounterUuid ??
93
+ initialWorkflowState.activeEncounterUuid,
94
+ patientUuids:
95
+ state.forms?.[state.activeFormUuid]?.patientUuids ??
96
+ initialWorkflowState.patientUuids,
97
+ encounters:
98
+ state.forms?.[state.activeFormUuid]?.encounters ??
99
+ initialWorkflowState.encounters,
100
+ }}
101
+ >
62
102
  {children}
63
103
  </FormWorkflowContext.Provider>
64
104
  );
@@ -1,76 +1,218 @@
1
+ import { initialWorkflowState } from "./FormWorkflowContext";
2
+
3
+ export const fdeWorkflowStorageVersion = "1.0.12";
4
+ export const fdeWorkflowStorageName = "openmrs:fastDataEntryWorkflowState";
5
+ const persistData = (data) => {
6
+ localStorage.setItem(fdeWorkflowStorageName, JSON.stringify(data));
7
+ };
8
+
9
+ const initialFormState = {
10
+ workflowState: "NEW_PATIENT",
11
+ activePatientUuid: null,
12
+ activeEncounterUuid: null,
13
+ patientUuids: [],
14
+ encounters: {},
15
+ };
16
+
1
17
  const reducer = (state, action) => {
2
18
  switch (action.type) {
3
- case "ADD_PATIENT":
4
- return {
19
+ case "INITIALIZE_WORKFLOW_STATE": {
20
+ const savedData = localStorage.getItem(fdeWorkflowStorageName);
21
+ const savedDataObject = savedData ? JSON.parse(savedData) : {};
22
+ let newState: { [key: string]: unknown } = {};
23
+ const newPatient = action.newPatientUuid
24
+ ? {
25
+ activePatientUuid: action.newPatientUuid,
26
+ workflowState: "EDIT_FORM",
27
+ }
28
+ : {};
29
+
30
+ if (
31
+ savedData &&
32
+ savedDataObject["_storageVersion"] === fdeWorkflowStorageVersion
33
+ ) {
34
+ // there is localStorage data and it is still valid
35
+ newState = {
36
+ ...savedDataObject,
37
+ activeFormUuid: action.activeFormUuid,
38
+ forms: {
39
+ ...savedDataObject.forms,
40
+ // initialize this particular form if it hasn't been created already
41
+ [action.activeFormUuid]: {
42
+ ...initialFormState,
43
+ ...savedDataObject.forms[action.activeFormUuid],
44
+ // if we receive activePatientUuid from a query parameter use that one
45
+ ...newPatient,
46
+ patientUuids:
47
+ savedDataObject.forms[action.activeFormUuid]?.patientUuids ||
48
+ initialFormState.patientUuids,
49
+ },
50
+ },
51
+ };
52
+ if (
53
+ action.newPatientUuid &&
54
+ !newState.forms[action.activeFormUuid].patientUuids.includes(
55
+ action.newPatientUuid
56
+ )
57
+ ) {
58
+ newState.forms[action.activeFormUuid].patientUuids.push(
59
+ action.newPatientUuid
60
+ );
61
+ }
62
+ } else {
63
+ // no localStorage data, or we should void it
64
+ newState = {
65
+ ...initialWorkflowState,
66
+ _storageVersion: fdeWorkflowStorageVersion,
67
+ forms: {
68
+ [action.activeFormUuid]: initialFormState,
69
+ },
70
+ activeFormUuid: action.activeFormUuid,
71
+ };
72
+ }
73
+ persistData(newState);
74
+ return { ...newState };
75
+ }
76
+ case "ADD_PATIENT": {
77
+ const newState = {
5
78
  ...state,
6
- patientUuids: [...state.patientUuids, action.patientUuid],
7
- activePatientUuid: action.patientUuid,
8
- activeEncounterUuid: null,
9
- workflowState: "EDIT_FORM",
79
+ forms: {
80
+ ...state.forms,
81
+ [state.activeFormUuid]: {
82
+ ...state.forms[state.activeFormUuid],
83
+ patientUuids: [
84
+ ...state.forms[state.activeFormUuid].patientUuids,
85
+ action.patientUuid,
86
+ ],
87
+ activePatientUuid: action.patientUuid,
88
+ activeEncounterUuid: null,
89
+ workflowState: "EDIT_FORM",
90
+ },
91
+ },
10
92
  };
11
- case "OPEN_PATIENT_SEARCH":
12
- // this will need to be updated once AMPATH hook is available
13
- return {
93
+ persistData(newState);
94
+ return newState;
95
+ }
96
+ case "OPEN_PATIENT_SEARCH": {
97
+ const newState = {
14
98
  ...state,
15
- activePatientUuid: null,
16
- activeEncounterUuid: null,
17
- workflowState: "NEW_PATIENT",
99
+ forms: {
100
+ ...state.forms,
101
+ [state.activeFormUuid]: {
102
+ ...state.forms[state.activeFormUuid],
103
+ activePatientUuid: null,
104
+ activeEncounterUuid: null,
105
+ workflowState: "NEW_PATIENT",
106
+ },
107
+ },
18
108
  };
19
- case "SAVE_ENCOUNTER":
20
- return {
109
+ // the persist here is optional...
110
+ persistData(newState);
111
+ return newState;
112
+ }
113
+ case "SAVE_ENCOUNTER": {
114
+ const newState = {
21
115
  ...state,
22
- encounters: {
23
- ...state.encounters,
24
- [state.activePatientUuid]: action.encounterUuid,
116
+ forms: {
117
+ ...state.forms,
118
+ [state.activeFormUuid]: {
119
+ ...state.forms[state.activeFormUuid],
120
+ encounters: {
121
+ ...state.forms[state.activeFormUuid].encounters,
122
+ [state.forms[state.activeFormUuid].activePatientUuid]:
123
+ action.encounterUuid,
124
+ },
125
+ activePatientUuid: null,
126
+ activeEncounterUuid: null,
127
+ workflowState:
128
+ state.forms[state.activeFormUuid].workflowState ===
129
+ "SUBMIT_FOR_NEXT"
130
+ ? "NEW_PATIENT"
131
+ : state.forms[state.activeFormUuid].workflowState ===
132
+ "SUBMIT_FOR_REVIEW"
133
+ ? "REVIEW"
134
+ : state.forms[state.activeFormUuid].workflowState,
135
+ },
25
136
  },
26
- activePatientUuid: null,
27
- activeEncounterUuid: null,
28
- workflowState:
29
- state.workflowState === "SUBMIT_FOR_NEXT"
30
- ? "NEW_PATIENT"
31
- : state.workflowState === "SUBMIT_FOR_REVIEW"
32
- ? "REVIEW"
33
- : state.workflowState,
34
137
  };
35
- case "EDIT_ENCOUNTER":
36
- return {
138
+ persistData(newState);
139
+ return newState;
140
+ }
141
+ case "EDIT_ENCOUNTER": {
142
+ const newState = {
37
143
  ...state,
38
- activeEncounterUuid: state.encounters[action.patientUuid],
39
- activePatientUuid: action.patientUuid,
40
- workflowState: "EDIT_FORM",
144
+ forms: {
145
+ ...state.forms,
146
+ [state.activeFormUuid]: {
147
+ ...state.forms[state.activeFormUuid],
148
+ activeEncounterUuid:
149
+ state.forms[state.activeFormUuid].encounters[action.patientUuid],
150
+ activePatientUuid: action.patientUuid,
151
+ workflowState: "EDIT_FORM",
152
+ },
153
+ },
41
154
  };
155
+ persistData(newState);
156
+ return newState;
157
+ }
42
158
  case "SUBMIT_FOR_NEXT":
159
+ // this state should not be persisted
43
160
  window.dispatchEvent(
44
161
  new CustomEvent("ampath-form-action", {
45
- detail: { formUuid: state.formUuid, action: "onSubmit" },
162
+ detail: {
163
+ formUuid: state.activeFormUuid,
164
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
165
+ action: "onSubmit",
166
+ },
46
167
  })
47
168
  );
48
169
  return {
49
170
  ...state,
50
- workflowState: "SUBMIT_FOR_NEXT",
171
+ forms: {
172
+ ...state.forms,
173
+ [state.activeFormUuid]: {
174
+ ...state.forms[state.activeFormUuid],
175
+ workflowState: "SUBMIT_FOR_NEXT",
176
+ },
177
+ },
51
178
  };
52
179
  case "SUBMIT_FOR_REVIEW":
180
+ // this state should not be persisted
53
181
  window.dispatchEvent(
54
182
  new CustomEvent("ampath-form-action", {
55
- detail: { formUuid: state.formUuid, action: "onSubmit" },
183
+ detail: {
184
+ formUuid: state.activeFormUuid,
185
+ patientUuid: state.forms[state.activeFormUuid].activePatientUuid,
186
+ action: "onSubmit",
187
+ },
56
188
  })
57
189
  );
58
190
  return {
59
191
  ...state,
60
- workflowState: "SUBMIT_FOR_REVIEW",
61
- };
62
- case "UPDATE_FORM_UUID":
63
- return {
64
- ...state,
65
- formUuid: action.formUuid,
192
+ forms: {
193
+ ...state.forms,
194
+ [state.activeFormUuid]: {
195
+ ...state.forms[state.activeFormUuid],
196
+ workflowState: "SUBMIT_FOR_REVIEW",
197
+ },
198
+ },
66
199
  };
67
- case "GO_TO_REVIEW":
68
- return {
200
+ case "GO_TO_REVIEW": {
201
+ const newState = {
69
202
  ...state,
70
- activeEncounterUuid: null,
71
- activePatientUuid: null,
72
- workflowState: "REVIEW",
203
+ forms: {
204
+ ...state.forms,
205
+ [state.activeFormUuid]: {
206
+ ...state.forms[state.activeFormUuid],
207
+ activeEncounterUuid: null,
208
+ activePatientUuid: null,
209
+ workflowState: "REVIEW",
210
+ },
211
+ },
73
212
  };
213
+ persistData(newState);
214
+ return newState;
215
+ }
74
216
  default:
75
217
  return state;
76
218
  }
@@ -21,7 +21,7 @@ const formStore = getGlobalStore("ampath-form-state");
21
21
 
22
22
  const WorkflowNavigationButtons = () => {
23
23
  const {
24
- formUuid,
24
+ activeFormUuid,
25
25
  submitForReview,
26
26
  submitForNext,
27
27
  workflowState,
@@ -29,10 +29,12 @@ const WorkflowNavigationButtons = () => {
29
29
  } = useContext(FormWorkflowContext);
30
30
  const history = useHistory();
31
31
  const store = useStore(formStore);
32
- const formState = store[formUuid];
32
+ const formState = store[activeFormUuid];
33
33
  const navigationDisabled = formState !== "ready";
34
34
  const { t } = useTranslation();
35
35
 
36
+ if (!workflowState) return null;
37
+
36
38
  return (
37
39
  <div className={styles.rightPanelActionButtons}>
38
40
  <Button
@@ -70,7 +72,7 @@ const FormWorkspace = () => {
70
72
  activePatientUuid,
71
73
  activeEncounterUuid,
72
74
  saveEncounter,
73
- formUuid,
75
+ activeFormUuid,
74
76
  } = useContext(FormWorkflowContext);
75
77
  const { t } = useTranslation();
76
78
 
@@ -94,7 +96,7 @@ const FormWorkspace = () => {
94
96
  patientUuid={activePatientUuid}
95
97
  encounterUuid={activeEncounterUuid}
96
98
  {...{
97
- formUuid,
99
+ formUuid: activeFormUuid,
98
100
  handlePostResponse,
99
101
  }}
100
102
  />
@@ -6,6 +6,10 @@ import { useGetAllForms } from "../hooks";
6
6
  import FormsTable from "../forms-table";
7
7
  import styles from "./styles.scss";
8
8
  import { useTranslation } from "react-i18next";
9
+ import {
10
+ fdeWorkflowStorageName,
11
+ fdeWorkflowStorageVersion,
12
+ } from "../context/FormWorkflowReducer";
9
13
 
10
14
  // helper function useful for debugging
11
15
  // given a list of forms, it will organize into permissions
@@ -41,6 +45,18 @@ const FormsPage = () => {
41
45
  const { formCategories, formCategoriesToShow } = config;
42
46
  const { forms, isLoading, error } = useGetAllForms();
43
47
  const cleanRows = prepareRowsForTable(forms);
48
+ const savedData = localStorage.getItem(fdeWorkflowStorageName);
49
+ const activeForms = [];
50
+ if (
51
+ savedData &&
52
+ JSON.parse(savedData)?.["_storageVersion"] === fdeWorkflowStorageVersion
53
+ ) {
54
+ Object.entries(JSON.parse(savedData).forms).forEach(
55
+ ([formUuid, form]: [string, { [key: string]: unknown }]) => {
56
+ if (form.workflowState) activeForms.push(formUuid);
57
+ }
58
+ );
59
+ }
44
60
 
45
61
  const categoryRows = formCategoriesToShow.map((name) => {
46
62
  const category = formCategories.find((category) => category.name === name);
@@ -61,11 +77,14 @@ const FormsPage = () => {
61
77
  cleanRows ? cleanRows?.length : "??"
62
78
  })`}
63
79
  >
64
- <FormsTable rows={cleanRows} {...{ error, isLoading }} />
80
+ <FormsTable rows={cleanRows} {...{ error, isLoading, activeForms }} />
65
81
  </Tab>
66
82
  {categoryRows?.map((category, index) => (
67
83
  <Tab label={`${category.name} (${category.rows.length})`} key={index}>
68
- <FormsTable rows={category.rows} {...{ error, isLoading }} />
84
+ <FormsTable
85
+ rows={category.rows}
86
+ {...{ error, isLoading, activeForms }}
87
+ />
69
88
  </Tab>
70
89
  ))}
71
90
  </Tabs>
@@ -19,7 +19,7 @@ import { Link } from "react-router-dom";
19
19
  import EmptyState from "../empty-state/EmptyState";
20
20
  import styles from "./styles.scss";
21
21
 
22
- const FormsTable = ({ rows, error, isLoading }) => {
22
+ const FormsTable = ({ rows, error, isLoading, activeForms }) => {
23
23
  const { t } = useTranslation();
24
24
 
25
25
  const formsHeader = [
@@ -39,7 +39,13 @@ const FormsTable = ({ rows, error, isLoading }) => {
39
39
 
40
40
  const augmenteRows = rows?.map((row) => ({
41
41
  ...row,
42
- actions: <Link to={row.uuid}>{t("fillForm", "Fill Form")}</Link>,
42
+ actions: (
43
+ <Link to={row.uuid}>
44
+ {activeForms.includes(row.uuid)
45
+ ? t("resumeSession", "Resume Session")
46
+ : t("fillForm", "Fill Form")}
47
+ </Link>
48
+ ),
43
49
  actions2: (
44
50
  <Link to="#" className={styles.inactiveLink}>
45
51
  {t("startGroupSession", "Start Group Session")}
@@ -4,11 +4,11 @@ import FormWorkflowContext from "../context/FormWorkflowContext";
4
4
  import useGetPatient from "../hooks/useGetPatient";
5
5
  import styles from "./styles.scss";
6
6
 
7
- const CardContainer = ({ active, onClick, children }) => {
7
+ const CardContainer = ({ onClick = () => undefined, active, children }) => {
8
8
  return (
9
9
  <div
10
+ onClick={onClick}
10
11
  className={`${styles.cardContainer} ${!active && styles.inactiveCard}`}
11
- onClick={active ? onClick : () => undefined}
12
12
  role="button"
13
13
  tabIndex={0}
14
14
  >
@@ -26,7 +26,7 @@ const PatientCard = ({ patientUuid }) => {
26
26
 
27
27
  if (!patient) {
28
28
  return (
29
- <CardContainer onClick={() => undefined} active={true}>
29
+ <CardContainer active={true}>
30
30
  <SkeletonText className={styles.skeletonText} />
31
31
  </CardContainer>
32
32
  );
@@ -35,7 +35,10 @@ const PatientCard = ({ patientUuid }) => {
35
35
  const active = activePatientUuid === patientUuid;
36
36
 
37
37
  return (
38
- <CardContainer onClick={() => editEncounter(patientUuid)} active={active}>
38
+ <CardContainer
39
+ onClick={active ? () => undefined : () => editEncounter(patientUuid)}
40
+ active={active}
41
+ >
39
42
  <div className={styles.identifier}>{identifier}</div>
40
43
  <div
41
44
  className={`${styles.displayName} ${
@@ -1,5 +1,9 @@
1
1
  import { Add20, Close20 } from "@carbon/icons-react";
2
- import { ExtensionSlot } from "@openmrs/esm-framework";
2
+ import {
3
+ ExtensionSlot,
4
+ interpolateUrl,
5
+ navigate,
6
+ } from "@openmrs/esm-framework";
3
7
  import { Button } from "carbon-components-react";
4
8
  import React, { useContext } from "react";
5
9
  import { Link } from "react-router-dom";
@@ -7,13 +11,21 @@ import FormWorkflowContext from "../context/FormWorkflowContext";
7
11
  import styles from "./styles.scss";
8
12
 
9
13
  const PatientSearchHeader = () => {
10
- const { addPatient, workflowState } = useContext(FormWorkflowContext);
14
+ const { addPatient, workflowState, activeFormUuid } =
15
+ useContext(FormWorkflowContext);
11
16
  const handleSelectPatient = (uuid) => {
12
17
  addPatient(uuid);
13
18
  };
14
19
 
15
20
  if (workflowState !== "NEW_PATIENT") return null;
16
21
 
22
+ const afterUrl = encodeURIComponent(
23
+ `\${openmrsSpaBase}/forms/${activeFormUuid}?patientUuid=\${patientUuid}`
24
+ );
25
+ const patientRegistrationUrl = interpolateUrl(
26
+ `\${openmrsSpaBase}/patient-registration?afterUrl=${afterUrl}`
27
+ );
28
+
17
29
  return (
18
30
  <div className={styles.searchHeaderContainer}>
19
31
  <span className={styles.padded}>Next patient:</span>
@@ -30,7 +42,7 @@ const PatientSearchHeader = () => {
30
42
  </span>
31
43
  <span className={styles.padded}>or</span>
32
44
  <span>
33
- <Button disabled>
45
+ <Button onClick={() => navigate({ to: patientRegistrationUrl })}>
34
46
  Create new patient <Add20 />
35
47
  </Button>
36
48
  </span>
@@ -9,6 +9,7 @@
9
9
  "goToForm": "Go To Form",
10
10
  "noFormsFound": "No Forms To Show",
11
11
  "noFormsFoundMessage": "No forms could be found for this category. Please double check the form concept uuids and access permissions.",
12
+ "resumeSession": "Resume Session",
12
13
  "selectPatientFirst": "Please select a patient first",
13
14
  "startGroupSession": "Start Group Session"
14
15
  }