@ltht-react/plan-definition-editor 2.0.3 → 2.0.5
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/package.json +8 -7
- package/src/index.tsx +237 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ltht-react/plan-definition-editor",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "ltht-react component for selecting problems from a PlanDefinition.",
|
|
5
5
|
"author": "LTHT",
|
|
6
6
|
"homepage": "",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"typings": "lib/index.d.ts",
|
|
10
10
|
"files": [
|
|
11
|
-
"lib"
|
|
11
|
+
"lib",
|
|
12
|
+
"src"
|
|
12
13
|
],
|
|
13
14
|
"directories": {
|
|
14
15
|
"lib": "lib"
|
|
@@ -27,11 +28,11 @@
|
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@emotion/react": "^11.0.0",
|
|
29
30
|
"@emotion/styled": "^11.0.0",
|
|
30
|
-
"@ltht-react/icon": "^2.0.
|
|
31
|
-
"@ltht-react/input": "^2.0.
|
|
32
|
-
"@ltht-react/styles": "^2.0.
|
|
33
|
-
"@ltht-react/types": "^2.0.
|
|
31
|
+
"@ltht-react/icon": "^2.0.5",
|
|
32
|
+
"@ltht-react/input": "^2.0.5",
|
|
33
|
+
"@ltht-react/styles": "^2.0.5",
|
|
34
|
+
"@ltht-react/types": "^2.0.5",
|
|
34
35
|
"react": "^18.2.0"
|
|
35
36
|
},
|
|
36
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "5d14d78829abb529afe382226925f4793a2c8257"
|
|
37
38
|
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { css } from '@emotion/react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
import { PlanDefinition } from '@ltht-react/types'
|
|
4
|
+
import { BANNER_COLOURS, WIDESCREEN_MINIMUM_MEDIA_QUERY } from '@ltht-react/styles'
|
|
5
|
+
import Icon from '@ltht-react/icon'
|
|
6
|
+
import { Toggle as ToggleInput } from '@ltht-react/input'
|
|
7
|
+
import { ChangeEvent, FC, useMemo } from 'react'
|
|
8
|
+
|
|
9
|
+
const toggleRowWidth = '3.5rem'
|
|
10
|
+
|
|
11
|
+
const toggleInputMixin = css`
|
|
12
|
+
> div:first-of-type {
|
|
13
|
+
text-align: right;
|
|
14
|
+
width: ${toggleRowWidth};
|
|
15
|
+
padding-right: 1rem;
|
|
16
|
+
}
|
|
17
|
+
`
|
|
18
|
+
|
|
19
|
+
const StyledProblemToggleRow = styled.div`
|
|
20
|
+
display: flex;
|
|
21
|
+
font-weight: bold;
|
|
22
|
+
align-items: center;
|
|
23
|
+
${toggleInputMixin}
|
|
24
|
+
margin-bottom: 0.25rem;
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
const StyledEducationToggleRow = styled.div`
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
${toggleInputMixin}
|
|
31
|
+
margin-bottom: 0.25rem;
|
|
32
|
+
|
|
33
|
+
> label svg {
|
|
34
|
+
margin-right: 0.5rem;
|
|
35
|
+
}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
const StyledTargetRow = styled.div`
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
${toggleInputMixin}
|
|
42
|
+
|
|
43
|
+
svg {
|
|
44
|
+
align: right;
|
|
45
|
+
margin-right: 1rem;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
> span:first-of-type {
|
|
49
|
+
text-align: right;
|
|
50
|
+
display: block;
|
|
51
|
+
width: ${toggleRowWidth};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
> div:first-of-type {
|
|
55
|
+
flex: 1;
|
|
56
|
+
text-align: left;
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
const StyledListItem = styled.li`
|
|
61
|
+
margin-bottom: 1rem;
|
|
62
|
+
|
|
63
|
+
${WIDESCREEN_MINIMUM_MEDIA_QUERY} {
|
|
64
|
+
}
|
|
65
|
+
`
|
|
66
|
+
|
|
67
|
+
const StyledSectionToggle = styled.div`
|
|
68
|
+
background-color: ${BANNER_COLOURS.INFO.BACKGROUND};
|
|
69
|
+
font-weight: bold;
|
|
70
|
+
color: black;
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
padding: 0.5rem 0.25rem;
|
|
74
|
+
${toggleInputMixin}
|
|
75
|
+
`
|
|
76
|
+
|
|
77
|
+
const StyledProblemSection = styled.ul`
|
|
78
|
+
list-style: none;
|
|
79
|
+
padding: 0.25rem 0.25rem;
|
|
80
|
+
margin-bottom: 1rem;
|
|
81
|
+
`
|
|
82
|
+
|
|
83
|
+
export const SelectedPlanDetail: FC<ISelectedPlanDetailProps> = ({
|
|
84
|
+
planDefinition,
|
|
85
|
+
selectedProblemIds,
|
|
86
|
+
onProblemChange,
|
|
87
|
+
}) => {
|
|
88
|
+
// Split actions from plan definition into clinical problem based and education based
|
|
89
|
+
const { problemActions, educationActions } = useMemo(() => {
|
|
90
|
+
const problemActions = planDefinition.action?.filter(
|
|
91
|
+
(action) =>
|
|
92
|
+
action?.code && action?.code[0] && action?.code[0].coding && action?.code[0].coding[0]?.code === 'problem'
|
|
93
|
+
)
|
|
94
|
+
const educationActions = planDefinition.action?.filter(
|
|
95
|
+
(action) =>
|
|
96
|
+
action?.code && action?.code[0] && action?.code[0].coding && action?.code[0].coding[0]?.code === 'education'
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
problemActions,
|
|
101
|
+
educationActions,
|
|
102
|
+
}
|
|
103
|
+
}, [planDefinition.action])
|
|
104
|
+
|
|
105
|
+
// Calculate whether all problems in the given categories are enabled
|
|
106
|
+
const { allProblemsOn, allEducationOn } = useMemo(
|
|
107
|
+
() => ({
|
|
108
|
+
allProblemsOn:
|
|
109
|
+
problemActions?.map((pa) => pa?.elementId as string).every((id) => selectedProblemIds.includes(id)) ?? false,
|
|
110
|
+
allEducationOn:
|
|
111
|
+
educationActions?.map((ea) => ea?.elementId as string).every((id) => selectedProblemIds.includes(id)) ?? false,
|
|
112
|
+
}),
|
|
113
|
+
[educationActions, problemActions, selectedProblemIds]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const handleSingleActionChange: OnProblemChange = (e) => {
|
|
117
|
+
onProblemChange(e)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const handleToggleAllProblemsClick = (e: ChangeEvent<HTMLInputElement>) => {
|
|
121
|
+
const newState = problemActions?.map((p) => ({ problemId: p?.elementId as string, state: e.target.checked }))
|
|
122
|
+
newState && onProblemChange(newState)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const handleToggleAllEducationClick = (e: ChangeEvent<HTMLInputElement>) => {
|
|
126
|
+
const newState = educationActions?.map((p) => ({ problemId: p?.elementId as string, state: e.target.checked }))
|
|
127
|
+
|
|
128
|
+
newState && onProblemChange(newState)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<>
|
|
133
|
+
<StyledSectionToggle>
|
|
134
|
+
<div>
|
|
135
|
+
<ToggleInput
|
|
136
|
+
checked={allProblemsOn}
|
|
137
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => handleToggleAllProblemsClick(e)}
|
|
138
|
+
id="problem-toggle-all"
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
<label htmlFor="problem-toggle-all">Patient Problems</label>
|
|
142
|
+
</StyledSectionToggle>
|
|
143
|
+
<StyledProblemSection>
|
|
144
|
+
{problemActions?.map((problem, pidx) => {
|
|
145
|
+
const goal = planDefinition.goal?.find(
|
|
146
|
+
(g) => problem?.goalId && problem.goalId.length > 0 && g?.elementId?.includes(problem.goalId[0] as string)
|
|
147
|
+
)
|
|
148
|
+
return (
|
|
149
|
+
<StyledListItem key={`problem-${pidx}`}>
|
|
150
|
+
<StyledProblemToggleRow>
|
|
151
|
+
<div>
|
|
152
|
+
<ToggleInput
|
|
153
|
+
checked={selectedProblemIds.includes(problem?.elementId as string)}
|
|
154
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
155
|
+
handleSingleActionChange([{ problemId: problem?.elementId as string, state: e.target.checked }])
|
|
156
|
+
}
|
|
157
|
+
id={`problem-${pidx}`}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
<label htmlFor={`problem-${pidx}`}>{problem?.description}</label>
|
|
161
|
+
</StyledProblemToggleRow>
|
|
162
|
+
<div>
|
|
163
|
+
<StyledTargetRow>
|
|
164
|
+
<span>
|
|
165
|
+
<Icon type="bullseye" size="large" />
|
|
166
|
+
</span>
|
|
167
|
+
<div>{goal?.description?.text}</div>
|
|
168
|
+
</StyledTargetRow>
|
|
169
|
+
|
|
170
|
+
{problem?.action?.map((interventionPlan, ipidx) => (
|
|
171
|
+
<StyledTargetRow key={`intervention-plan-${ipidx}`}>
|
|
172
|
+
<div>
|
|
173
|
+
<p>{interventionPlan?.description}</p>
|
|
174
|
+
<ul>
|
|
175
|
+
{interventionPlan?.action?.map((intervention, idx) => (
|
|
176
|
+
<li key={`intervention-${idx}`}>{intervention?.description}</li>
|
|
177
|
+
))}
|
|
178
|
+
</ul>
|
|
179
|
+
</div>
|
|
180
|
+
</StyledTargetRow>
|
|
181
|
+
))}
|
|
182
|
+
</div>
|
|
183
|
+
</StyledListItem>
|
|
184
|
+
)
|
|
185
|
+
})}
|
|
186
|
+
</StyledProblemSection>
|
|
187
|
+
<StyledSectionToggle>
|
|
188
|
+
<div>
|
|
189
|
+
<ToggleInput
|
|
190
|
+
checked={allEducationOn}
|
|
191
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) => handleToggleAllEducationClick(e)}
|
|
192
|
+
id="education-toggle-all"
|
|
193
|
+
/>
|
|
194
|
+
</div>
|
|
195
|
+
<label htmlFor="education-toggle-all">Patient Education</label>
|
|
196
|
+
</StyledSectionToggle>
|
|
197
|
+
<StyledProblemSection>
|
|
198
|
+
<StyledListItem>
|
|
199
|
+
<div>
|
|
200
|
+
{educationActions?.map((g, eaidx) => (
|
|
201
|
+
<StyledEducationToggleRow key={`education-${eaidx}`}>
|
|
202
|
+
<div>
|
|
203
|
+
<ToggleInput
|
|
204
|
+
checked={selectedProblemIds.includes(g?.elementId as string)}
|
|
205
|
+
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
206
|
+
handleSingleActionChange([{ problemId: g?.elementId as string, state: e.target.checked }])
|
|
207
|
+
}
|
|
208
|
+
id={`education-${eaidx}`}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
<label htmlFor={`education-${eaidx}`}>
|
|
212
|
+
<Icon type="bullseye" size="large" />
|
|
213
|
+
{g?.description}
|
|
214
|
+
</label>
|
|
215
|
+
</StyledEducationToggleRow>
|
|
216
|
+
))}
|
|
217
|
+
</div>
|
|
218
|
+
</StyledListItem>
|
|
219
|
+
</StyledProblemSection>
|
|
220
|
+
</>
|
|
221
|
+
)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface ProblemState {
|
|
225
|
+
problemId: string
|
|
226
|
+
state: boolean
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export type OnProblemChange = (e: ProblemState[]) => void
|
|
230
|
+
|
|
231
|
+
export interface ISelectedPlanDetailProps {
|
|
232
|
+
planDefinition: PlanDefinition
|
|
233
|
+
selectedProblemIds: string[]
|
|
234
|
+
onProblemChange: OnProblemChange
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export default SelectedPlanDetail
|