@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.
- package/dist/247.js +1 -1
- package/dist/557.js +1 -0
- package/dist/574.js +1 -1
- package/dist/592.js +1 -0
- package/dist/804.js +1 -1
- package/dist/990.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +55 -33
- package/dist/openmrs-esm-fast-data-entry-app.old +1 -1
- package/package.json +1 -1
- package/src/context/FormWorkflowContext.tsx +59 -19
- package/src/context/FormWorkflowReducer.ts +186 -44
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +6 -4
- package/src/forms-page/FormsPage.tsx +21 -2
- package/src/forms-table/FormsTable.tsx +8 -2
- package/src/patient-card/PatientCard.tsx +7 -4
- package/src/patient-search-header/PatientSearchHeader.tsx +15 -3
- package/translations/en.json +1 -0
- package/dist/61.js +0 -1
|
@@ -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.
|
|
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,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
|
|
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
|
|
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
|
|
28
|
-
const
|
|
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 (
|
|
52
|
-
|
|
75
|
+
if (state?.workflowState === null && activeFormUuid) {
|
|
76
|
+
actions.initializeWorkflowState({ activeFormUuid, newPatientUuid });
|
|
53
77
|
}
|
|
54
|
-
}, [
|
|
55
|
-
|
|
56
|
-
useEffect(() => {
|
|
57
|
-
actions.openPatientSearch();
|
|
58
|
-
}, [actions]);
|
|
78
|
+
}, [activeFormUuid, newPatientUuid, state?.workflowState, actions]);
|
|
59
79
|
|
|
60
80
|
return (
|
|
61
|
-
<FormWorkflowContext.Provider
|
|
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 "
|
|
4
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
93
|
+
persistData(newState);
|
|
94
|
+
return newState;
|
|
95
|
+
}
|
|
96
|
+
case "OPEN_PATIENT_SEARCH": {
|
|
97
|
+
const newState = {
|
|
14
98
|
...state,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
109
|
+
// the persist here is optional...
|
|
110
|
+
persistData(newState);
|
|
111
|
+
return newState;
|
|
112
|
+
}
|
|
113
|
+
case "SAVE_ENCOUNTER": {
|
|
114
|
+
const newState = {
|
|
21
115
|
...state,
|
|
22
|
-
|
|
23
|
-
...state.
|
|
24
|
-
[state.
|
|
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
|
-
|
|
36
|
-
return
|
|
138
|
+
persistData(newState);
|
|
139
|
+
return newState;
|
|
140
|
+
}
|
|
141
|
+
case "EDIT_ENCOUNTER": {
|
|
142
|
+
const newState = {
|
|
37
143
|
...state,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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: {
|
|
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
|
-
|
|
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: {
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
200
|
+
case "GO_TO_REVIEW": {
|
|
201
|
+
const newState = {
|
|
69
202
|
...state,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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[
|
|
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
|
-
|
|
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
|
|
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:
|
|
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 = ({
|
|
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
|
|
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
|
|
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 {
|
|
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 } =
|
|
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
|
|
45
|
+
<Button onClick={() => navigate({ to: patientRegistrationUrl })}>
|
|
34
46
|
Create new patient <Add20 />
|
|
35
47
|
</Button>
|
|
36
48
|
</span>
|
package/translations/en.json
CHANGED
|
@@ -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
|
}
|