@aehrc/smart-forms-renderer 0.40.0 → 0.40.2
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/lib/components/FormComponents/ItemParts/ItemFieldGrid.js +6 -2
- package/lib/components/FormComponents/ItemParts/ItemFieldGrid.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js +3 -3
- package/lib/components/FormComponents/RepeatGroup/RepeatGroup.js.map +1 -1
- package/lib/components/FormComponents/RepeatGroup/RepeatGroupView.js +6 -6
- package/lib/components/FormComponents/RepeatGroup/RepeatGroupView.js.map +1 -1
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js +5 -5
- package/lib/components/FormComponents/RepeatItem/RepeatItem.js.map +1 -1
- package/lib/components/FormComponents/Tables/GroupTable.js +10 -10
- package/lib/components/FormComponents/Tables/GroupTable.js.map +1 -1
- package/lib/components/FormComponents/Tables/GroupTableBody.d.ts +1 -1
- package/lib/components/FormComponents/Tables/GroupTableBody.js +3 -3
- package/lib/components/FormComponents/Tables/GroupTableBody.js.map +1 -1
- package/lib/components/FormComponents/Tables/GroupTableRow.d.ts +1 -1
- package/lib/components/FormComponents/Tables/GroupTableRow.js +4 -4
- package/lib/components/FormComponents/Tables/GroupTableRow.js.map +1 -1
- package/lib/components/FormComponents/Tables/GroupTableView.d.ts +1 -1
- package/lib/components/Renderer/FormTopLevelItem.js +13 -1
- package/lib/components/Renderer/FormTopLevelItem.js.map +1 -1
- package/lib/hooks/useDateValidation.js +2 -2
- package/lib/hooks/useDateValidation.js.map +1 -1
- package/lib/hooks/useGroupTableRows.d.ts +1 -1
- package/lib/hooks/useGroupTableRows.js +3 -3
- package/lib/hooks/useGroupTableRows.js.map +1 -1
- package/lib/hooks/useInitialiseGroupTable.d.ts +1 -1
- package/lib/hooks/useInitialiseGroupTable.js +10 -16
- package/lib/hooks/useInitialiseGroupTable.js.map +1 -1
- package/lib/hooks/useInitialiseRepeatAnswers.d.ts +1 -1
- package/lib/hooks/useInitialiseRepeatAnswers.js +9 -12
- package/lib/hooks/useInitialiseRepeatAnswers.js.map +1 -1
- package/lib/hooks/useInitialiseRepeatGroups.d.ts +2 -2
- package/lib/hooks/useInitialiseRepeatGroups.js +11 -20
- package/lib/hooks/useInitialiseRepeatGroups.js.map +1 -1
- package/lib/hooks/useRepeatGroups.js +6 -2
- package/lib/hooks/useRepeatGroups.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/interfaces/groupTable.interface.d.ts +1 -1
- package/lib/interfaces/repeatGroup.interface.d.ts +1 -1
- package/lib/theme/palette.js +0 -4
- package/lib/theme/palette.js.map +1 -1
- package/lib/utils/groupTable.js +1 -1
- package/lib/utils/groupTable.js.map +1 -1
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +1 -1
- package/lib/utils/index.js.map +1 -1
- package/lib/utils/manageForm.d.ts +7 -0
- package/lib/utils/manageForm.js +15 -1
- package/lib/utils/manageForm.js.map +1 -1
- package/lib/utils/repeatId.d.ts +4 -0
- package/lib/utils/repeatId.js +93 -0
- package/lib/utils/repeatId.js.map +1 -0
- package/package.json +1 -1
- package/src/components/FormComponents/ItemParts/ItemFieldGrid.tsx +8 -2
- package/src/components/FormComponents/RepeatGroup/RepeatGroup.tsx +3 -3
- package/src/components/FormComponents/RepeatGroup/RepeatGroupView.tsx +6 -6
- package/src/components/FormComponents/RepeatItem/RepeatItem.tsx +7 -5
- package/src/components/FormComponents/Tables/GroupTable.tsx +13 -10
- package/src/components/FormComponents/Tables/GroupTableBody.tsx +5 -5
- package/src/components/FormComponents/Tables/GroupTableRow.tsx +5 -5
- package/src/components/FormComponents/Tables/GroupTableView.tsx +1 -1
- package/src/components/Renderer/FormTopLevelItem.tsx +33 -1
- package/src/hooks/useDateValidation.tsx +2 -2
- package/src/hooks/useGroupTableRows.ts +3 -3
- package/src/hooks/useInitialiseGroupTable.ts +13 -17
- package/src/hooks/useInitialiseRepeatAnswers.ts +9 -12
- package/src/hooks/useInitialiseRepeatGroups.ts +14 -25
- package/src/hooks/useRepeatGroups.ts +8 -4
- package/src/index.ts +1 -0
- package/src/interfaces/groupTable.interface.ts +1 -1
- package/src/interfaces/repeatGroup.interface.ts +1 -1
- package/src/stories/assets/questionnaires/QIdRemoverDebugger.ts +161 -0
- package/src/stories/storybookWrappers/BuildFormButtonForStorybook.tsx +1 -1
- package/src/stories/storybookWrappers/IdRemoverButtonForStorybook.tsx +51 -0
- package/src/stories/storybookWrappers/IdRemoverDebuggerWrapperForStorybook.tsx +84 -0
- package/src/stories/testing/IdRemoverDebuggerWrapper.stories.tsx +39 -0
- package/src/tests/extractObservation.test.ts +2 -5
- package/src/theme/palette.ts +0 -4
- package/src/utils/groupTable.ts +1 -1
- package/src/utils/index.ts +7 -1
- package/src/utils/manageForm.ts +28 -1
- package/src/utils/repeatId.ts +123 -0
- package/CHANGELOG.md +0 -43
- package/lib/hooks/useRepeatAnswers.d.ts +0 -4
- package/lib/hooks/useRepeatAnswers.js +0 -34
- package/lib/hooks/useRepeatAnswers.js.map +0 -1
- package/lib/interfaces/repeatItem.interface.d.ts +0 -5
- package/lib/interfaces/repeatItem.interface.js +0 -2
- package/lib/interfaces/repeatItem.interface.js.map +0 -1
- package/lib/utils/answerExpression.d.ts +0 -18
- package/lib/utils/answerExpression.js +0 -133
- package/lib/utils/answerExpression.js.map +0 -1
- package/lib/utils/dynamicValueSet.d.ts +0 -5
- package/lib/utils/dynamicValueSet.js +0 -96
- package/lib/utils/dynamicValueSet.js.map +0 -1
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.d.ts +0 -14
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js +0 -639
- package/lib/utils/fhirpathAsyncUtils/fhirpath-async.js.map +0 -1
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.d.ts +0 -3
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.js +0 -41
- package/lib/utils/fhirpathAsyncUtils/outcome-utils.js.map +0 -1
- package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.d.ts +0 -3
- package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.js +0 -34
- package/lib/utils/questionnaireStoreUtils/extractPreferredTerminologyServerUrls.js.map +0 -1
- package/lib/utils/updateQr.d.ts +0 -9
- package/lib/utils/updateQr.js +0 -55
- package/lib/utils/updateQr.js.map +0 -1
|
@@ -25,13 +25,13 @@ import type {
|
|
|
25
25
|
PropsWithQrRepeatGroupChangeHandler,
|
|
26
26
|
PropsWithShowMinimalViewAttribute
|
|
27
27
|
} from '../../../interfaces/renderProps.interface';
|
|
28
|
-
import { nanoid } from 'nanoid';
|
|
29
28
|
import useReadOnly from '../../../hooks/useReadOnly';
|
|
30
29
|
import GroupTableView from './GroupTableView';
|
|
31
30
|
import type { GroupTableRowModel } from '../../../interfaces/groupTable.interface';
|
|
32
31
|
import { getGroupTableItemsToUpdate } from '../../../utils/groupTable';
|
|
33
32
|
import useGroupTableRows from '../../../hooks/useGroupTableRows';
|
|
34
33
|
import { flushSync } from 'react-dom';
|
|
34
|
+
import { generateNewRepeatId } from '../../../utils/repeatId';
|
|
35
35
|
|
|
36
36
|
interface GroupTableProps
|
|
37
37
|
extends PropsWithQrRepeatGroupChangeHandler,
|
|
@@ -62,7 +62,10 @@ function GroupTable(props: GroupTableProps) {
|
|
|
62
62
|
|
|
63
63
|
const readOnly = useReadOnly(qItem, parentIsReadOnly);
|
|
64
64
|
|
|
65
|
-
const { tableRows, selectedIds, setTableRows, setSelectedIds } = useGroupTableRows(
|
|
65
|
+
const { tableRows, selectedIds, setTableRows, setSelectedIds } = useGroupTableRows(
|
|
66
|
+
qItem.linkId,
|
|
67
|
+
qrItems
|
|
68
|
+
);
|
|
66
69
|
|
|
67
70
|
// Generate item labels as table headers
|
|
68
71
|
const qItems = qItem.item;
|
|
@@ -101,7 +104,7 @@ function GroupTable(props: GroupTableProps) {
|
|
|
101
104
|
const updatedTableRows = [...tableRows];
|
|
102
105
|
|
|
103
106
|
const rowToRemove = updatedTableRows[index];
|
|
104
|
-
const updatedSelectedIds = selectedIds.filter((id) => id !== rowToRemove.
|
|
107
|
+
const updatedSelectedIds = selectedIds.filter((id) => id !== rowToRemove.id);
|
|
105
108
|
|
|
106
109
|
updatedTableRows.splice(index, 1);
|
|
107
110
|
|
|
@@ -114,21 +117,21 @@ function GroupTable(props: GroupTableProps) {
|
|
|
114
117
|
}
|
|
115
118
|
|
|
116
119
|
function handleAddRow() {
|
|
117
|
-
const
|
|
120
|
+
const newRowId = generateNewRepeatId(qItem.linkId);
|
|
118
121
|
setTableRows([
|
|
119
122
|
...tableRows,
|
|
120
123
|
{
|
|
121
|
-
|
|
124
|
+
id: newRowId,
|
|
122
125
|
qrItem: null
|
|
123
126
|
}
|
|
124
127
|
]);
|
|
125
|
-
setSelectedIds([...selectedIds,
|
|
128
|
+
setSelectedIds([...selectedIds, newRowId]);
|
|
126
129
|
}
|
|
127
130
|
|
|
128
131
|
function handleSelectAll() {
|
|
129
132
|
// deselect all if all are selected, otherwise select all
|
|
130
133
|
const updatedTableIds =
|
|
131
|
-
selectedIds.length === tableRows.length ? [] : tableRows.map((tableRow) => tableRow.
|
|
134
|
+
selectedIds.length === tableRows.length ? [] : tableRows.map((tableRow) => tableRow.id);
|
|
132
135
|
setSelectedIds(updatedTableIds);
|
|
133
136
|
onQrRepeatGroupChange({
|
|
134
137
|
linkId: qItem.linkId,
|
|
@@ -136,12 +139,12 @@ function GroupTable(props: GroupTableProps) {
|
|
|
136
139
|
});
|
|
137
140
|
}
|
|
138
141
|
|
|
139
|
-
function handleSelectRow(
|
|
142
|
+
function handleSelectRow(rowId: string) {
|
|
140
143
|
const updatedSelectedIds = [...selectedIds];
|
|
141
144
|
|
|
142
|
-
const index = updatedSelectedIds.indexOf(
|
|
145
|
+
const index = updatedSelectedIds.indexOf(rowId);
|
|
143
146
|
if (index === -1) {
|
|
144
|
-
updatedSelectedIds.push(
|
|
147
|
+
updatedSelectedIds.push(rowId);
|
|
145
148
|
} else {
|
|
146
149
|
updatedSelectedIds.splice(index, 1);
|
|
147
150
|
}
|
|
@@ -41,7 +41,7 @@ interface GroupTableBodyProps
|
|
|
41
41
|
qItemsIndexMap: Record<string, number>;
|
|
42
42
|
onRowChange: (newQrRow: QuestionnaireResponseItem, index: number) => void;
|
|
43
43
|
onRemoveRow: (index: number) => void;
|
|
44
|
-
onSelectRow: (
|
|
44
|
+
onSelectRow: (rowId: string) => void;
|
|
45
45
|
onReorderRows: (newTableRows: GroupTableRowModel[]) => void;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -80,8 +80,8 @@ function GroupTableBody(props: GroupTableBodyProps) {
|
|
|
80
80
|
<Droppable droppableId="gtable_rows" direction="vertical">
|
|
81
81
|
{(droppableProvided, snapshot) => (
|
|
82
82
|
<TableBody ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
|
|
83
|
-
{tableRows.map(({
|
|
84
|
-
const itemIsSelected = selectedIds.indexOf(
|
|
83
|
+
{tableRows.map(({ id, qrItem: nullableQrItem }, index) => {
|
|
84
|
+
const itemIsSelected = selectedIds.indexOf(id) !== -1;
|
|
85
85
|
const answeredQrItem = createEmptyQrItem(tableQItem, undefined);
|
|
86
86
|
if (nullableQrItem) {
|
|
87
87
|
answeredQrItem.item = nullableQrItem.item;
|
|
@@ -89,9 +89,9 @@ function GroupTableBody(props: GroupTableBodyProps) {
|
|
|
89
89
|
|
|
90
90
|
return (
|
|
91
91
|
<GroupTableRow
|
|
92
|
-
key={
|
|
92
|
+
key={id}
|
|
93
93
|
index={index}
|
|
94
|
-
|
|
94
|
+
rowId={id}
|
|
95
95
|
tableQItem={tableQItem}
|
|
96
96
|
answeredQrItem={answeredQrItem}
|
|
97
97
|
nullableQrItem={nullableQrItem}
|
|
@@ -39,7 +39,7 @@ interface GroupTableRowProps
|
|
|
39
39
|
PropsWithShowMinimalViewAttribute,
|
|
40
40
|
PropsWithParentIsReadOnlyAttribute,
|
|
41
41
|
TableRowProps {
|
|
42
|
-
|
|
42
|
+
rowId: string;
|
|
43
43
|
index: number;
|
|
44
44
|
tableQItem: QuestionnaireItem;
|
|
45
45
|
answeredQrItem: QuestionnaireResponseItem;
|
|
@@ -58,7 +58,7 @@ interface GroupTableRowProps
|
|
|
58
58
|
|
|
59
59
|
function GroupTableRow(props: GroupTableRowProps) {
|
|
60
60
|
const {
|
|
61
|
-
|
|
61
|
+
rowId,
|
|
62
62
|
index,
|
|
63
63
|
tableQItem,
|
|
64
64
|
answeredQrItem,
|
|
@@ -78,7 +78,7 @@ function GroupTableRow(props: GroupTableRowProps) {
|
|
|
78
78
|
|
|
79
79
|
if (isRepeated) {
|
|
80
80
|
return (
|
|
81
|
-
<Draggable draggableId={
|
|
81
|
+
<Draggable draggableId={rowId} index={index}>
|
|
82
82
|
{(draggableProvided, snapshot) => (
|
|
83
83
|
<StyledGroupTableRow
|
|
84
84
|
itemIsDragged={snapshot.isDragging}
|
|
@@ -106,7 +106,7 @@ function GroupTableRow(props: GroupTableRowProps) {
|
|
|
106
106
|
<SelectRowButton
|
|
107
107
|
isSelected={itemIsSelected}
|
|
108
108
|
readOnly={readOnly}
|
|
109
|
-
onSelectItem={() => onSelectRow(
|
|
109
|
+
onSelectItem={() => onSelectRow(rowId)}
|
|
110
110
|
/>
|
|
111
111
|
</>
|
|
112
112
|
)}
|
|
@@ -150,7 +150,7 @@ function GroupTableRow(props: GroupTableRowProps) {
|
|
|
150
150
|
<SelectRowButton
|
|
151
151
|
isSelected={itemIsSelected}
|
|
152
152
|
readOnly={readOnly}
|
|
153
|
-
onSelectItem={() => onSelectRow(
|
|
153
|
+
onSelectItem={() => onSelectRow(rowId)}
|
|
154
154
|
/>
|
|
155
155
|
</>
|
|
156
156
|
)}
|
|
@@ -54,7 +54,7 @@ interface GroupTableViewProps
|
|
|
54
54
|
onAddRow: () => void;
|
|
55
55
|
onRowChange: (newQrRow: QuestionnaireResponseItem, index: number) => void;
|
|
56
56
|
onRemoveRow: (index: number) => void;
|
|
57
|
-
onSelectRow: (
|
|
57
|
+
onSelectRow: (rowId: string) => void;
|
|
58
58
|
onSelectAll: () => void;
|
|
59
59
|
onReorderRows: (newTableRows: GroupTableRowModel[]) => void;
|
|
60
60
|
}
|
|
@@ -34,8 +34,10 @@ import useHidden from '../../hooks/useHidden';
|
|
|
34
34
|
import GroupItemSwitcher from '../FormComponents/GroupItem/GroupItemSwitcher';
|
|
35
35
|
import useReadOnly from '../../hooks/useReadOnly';
|
|
36
36
|
import Box from '@mui/material/Box';
|
|
37
|
-
import { isSpecificItemControl } from '../../utils';
|
|
37
|
+
import { isRepeatItemAndNotCheckbox, isSpecificItemControl } from '../../utils';
|
|
38
38
|
import GroupTable from '../FormComponents/Tables/GroupTable';
|
|
39
|
+
import RepeatItem from '../FormComponents/RepeatItem/RepeatItem';
|
|
40
|
+
import GridGroup from '../FormComponents/GridGroup/GridGroup';
|
|
39
41
|
|
|
40
42
|
interface FormTopLevelItemProps
|
|
41
43
|
extends PropsWithQrItemChangeHandler,
|
|
@@ -127,6 +129,20 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
|
127
129
|
|
|
128
130
|
// If form is untabbed, it is rendered as a regular group
|
|
129
131
|
if (itemIsGroup) {
|
|
132
|
+
// Item is 'grid'
|
|
133
|
+
const itemIsGrid = isSpecificItemControl(topLevelQItem, 'grid');
|
|
134
|
+
if (itemIsGrid) {
|
|
135
|
+
return (
|
|
136
|
+
<GridGroup
|
|
137
|
+
qItem={topLevelQItem}
|
|
138
|
+
qrItem={topLevelQRItem}
|
|
139
|
+
groupCardElevation={1}
|
|
140
|
+
parentIsReadOnly={parentIsReadOnly}
|
|
141
|
+
onQrItemChange={onQrItemChange}
|
|
142
|
+
/>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
130
146
|
// GroupTable "gtable" can be rendered with either repeats:true or false
|
|
131
147
|
if (isSpecificItemControl(topLevelQItem, 'gtable')) {
|
|
132
148
|
return (
|
|
@@ -156,6 +172,22 @@ function FormTopLevelItem(props: FormTopLevelItemProps) {
|
|
|
156
172
|
}
|
|
157
173
|
|
|
158
174
|
// Otherwise, it is rendered as a non-group item
|
|
175
|
+
const itemRepeatsAndIsNotCheckbox = isRepeatItemAndNotCheckbox(topLevelQItem);
|
|
176
|
+
if (itemRepeatsAndIsNotCheckbox) {
|
|
177
|
+
return (
|
|
178
|
+
<Box mt={1}>
|
|
179
|
+
<RepeatItem
|
|
180
|
+
key={topLevelQItem.linkId}
|
|
181
|
+
qItem={topLevelQItem}
|
|
182
|
+
qrItem={topLevelQRItem}
|
|
183
|
+
groupCardElevation={1}
|
|
184
|
+
parentIsReadOnly={readOnly}
|
|
185
|
+
onQrItemChange={onQrItemChange}
|
|
186
|
+
/>
|
|
187
|
+
</Box>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
159
191
|
return (
|
|
160
192
|
<Box mt={1}>
|
|
161
193
|
<SingleItem
|
|
@@ -55,8 +55,8 @@ function useDateValidation(input: string, parseFail: boolean = false): string |
|
|
|
55
55
|
|
|
56
56
|
const matches = input.split('/');
|
|
57
57
|
|
|
58
|
-
if (validateTwoMatches(matches[0], matches[1])) {
|
|
59
|
-
return
|
|
58
|
+
if (!validateTwoMatches(matches[0], matches[1])) {
|
|
59
|
+
return 'Input is an invalid date.';
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
return null;
|
|
@@ -19,12 +19,12 @@ import { useState } from 'react';
|
|
|
19
19
|
import useInitialiseGroupTable from './useInitialiseGroupTable';
|
|
20
20
|
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
21
21
|
|
|
22
|
-
function useGroupTableRows(qrItems: QuestionnaireResponseItem[]) {
|
|
23
|
-
const initialisedGroupTableRows = useInitialiseGroupTable(qrItems);
|
|
22
|
+
function useGroupTableRows(linkId: string, qrItems: QuestionnaireResponseItem[]) {
|
|
23
|
+
const initialisedGroupTableRows = useInitialiseGroupTable(linkId, qrItems);
|
|
24
24
|
|
|
25
25
|
const [tableRows, setTableRows] = useState(initialisedGroupTableRows);
|
|
26
26
|
const [selectedIds, setSelectedIds] = useState<string[]>(
|
|
27
|
-
initialisedGroupTableRows.map((row) => row.
|
|
27
|
+
initialisedGroupTableRows.map((row) => row.id)
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
return { tableRows, selectedIds, setTableRows, setSelectedIds };
|
|
@@ -16,27 +16,23 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
19
|
-
import { nanoid } from 'nanoid';
|
|
20
19
|
import type { GroupTableRowModel } from '../interfaces/groupTable.interface';
|
|
20
|
+
import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
|
|
21
21
|
|
|
22
|
-
function useInitialiseGroupTable(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
];
|
|
29
|
-
|
|
30
|
-
if (qrItems.length > 0) {
|
|
31
|
-
initialGroupTableRows = qrItems.map((qrItem) => {
|
|
32
|
-
return {
|
|
33
|
-
nanoId: nanoid(),
|
|
34
|
-
qrItem
|
|
35
|
-
};
|
|
36
|
-
});
|
|
22
|
+
function useInitialiseGroupTable(
|
|
23
|
+
linkId: string,
|
|
24
|
+
qrItems: QuestionnaireResponseItem[]
|
|
25
|
+
): GroupTableRowModel[] {
|
|
26
|
+
if (qrItems.length === 0) {
|
|
27
|
+
return [{ id: generateNewRepeatId(linkId), qrItem: null }];
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
return
|
|
30
|
+
return qrItems.map((qrItem, index) => {
|
|
31
|
+
return {
|
|
32
|
+
id: generateExistingRepeatId(linkId, index),
|
|
33
|
+
qrItem
|
|
34
|
+
};
|
|
35
|
+
});
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
export default useInitialiseGroupTable;
|
|
@@ -16,26 +16,23 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import type { QuestionnaireResponseItem, QuestionnaireResponseItemAnswer } from 'fhir/r4';
|
|
19
|
-
import { nanoid } from 'nanoid';
|
|
20
19
|
import { useMemo } from 'react';
|
|
20
|
+
import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
|
|
21
21
|
|
|
22
22
|
function useInitialiseRepeatAnswers(
|
|
23
|
+
linkId: string,
|
|
23
24
|
qrItem: QuestionnaireResponseItem | null
|
|
24
25
|
): (QuestionnaireResponseItemAnswer | null)[] {
|
|
25
26
|
return useMemo(() => {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (qrItem?.answer) {
|
|
29
|
-
initialRepeatAnswers = qrItem.answer.map((answer) => {
|
|
30
|
-
if (!answer.id) {
|
|
31
|
-
answer.id = nanoid();
|
|
32
|
-
}
|
|
33
|
-
return answer;
|
|
34
|
-
});
|
|
27
|
+
if (!qrItem?.answer) {
|
|
28
|
+
return [{ id: generateNewRepeatId(linkId) }];
|
|
35
29
|
}
|
|
36
30
|
|
|
37
|
-
return
|
|
38
|
-
|
|
31
|
+
return qrItem.answer.map((answer, index) => ({
|
|
32
|
+
...answer,
|
|
33
|
+
id: answer.id ?? generateExistingRepeatId(linkId, index)
|
|
34
|
+
}));
|
|
35
|
+
}, [linkId, qrItem]);
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
export default useInitialiseRepeatAnswers;
|
|
@@ -15,38 +15,27 @@
|
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import type {
|
|
19
|
-
import { nanoid } from 'nanoid';
|
|
18
|
+
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
20
19
|
import type { RepeatGroupSingle } from '../interfaces/repeatGroup.interface';
|
|
21
20
|
import { useMemo } from 'react';
|
|
21
|
+
import { generateExistingRepeatId, generateNewRepeatId } from '../utils/repeatId';
|
|
22
22
|
|
|
23
23
|
function useInitialiseRepeatGroups(
|
|
24
|
-
|
|
24
|
+
linkId: string,
|
|
25
25
|
qrItems: QuestionnaireResponseItem[]
|
|
26
26
|
): RepeatGroupSingle[] {
|
|
27
|
-
return useMemo(
|
|
28
|
-
()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
nanoId: nanoid(),
|
|
32
|
-
qrItem: null
|
|
33
|
-
}
|
|
34
|
-
];
|
|
27
|
+
return useMemo(() => {
|
|
28
|
+
if (qrItems.length === 0) {
|
|
29
|
+
return [{ id: generateNewRepeatId(linkId), qrItem: null }];
|
|
30
|
+
}
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
return initialRepeatGroupAnswers;
|
|
45
|
-
},
|
|
46
|
-
// Requires checking of both qItem and qrItems
|
|
47
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
48
|
-
[qItem, qrItems]
|
|
49
|
-
);
|
|
32
|
+
return qrItems.map((qrItem, index) => {
|
|
33
|
+
return {
|
|
34
|
+
id: generateExistingRepeatId(linkId, index),
|
|
35
|
+
qrItem
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}, [linkId, qrItems]);
|
|
50
39
|
}
|
|
51
40
|
|
|
52
41
|
export default useInitialiseRepeatGroups;
|
|
@@ -19,6 +19,7 @@ import type { Dispatch, SetStateAction } from 'react';
|
|
|
19
19
|
import { useEffect, useState } from 'react';
|
|
20
20
|
import type { RepeatGroupSingle } from '../interfaces/repeatGroup.interface';
|
|
21
21
|
import _isEqual from 'lodash/isEqual';
|
|
22
|
+
import type { QuestionnaireResponseItem } from 'fhir/r4';
|
|
22
23
|
|
|
23
24
|
function useRepeatGroups(
|
|
24
25
|
valueFromProps: RepeatGroupSingle[]
|
|
@@ -27,10 +28,13 @@ function useRepeatGroups(
|
|
|
27
28
|
|
|
28
29
|
useEffect(
|
|
29
30
|
() => {
|
|
30
|
-
const valueFromPropsQRItems = valueFromProps
|
|
31
|
-
(repeatGroupSingle) => repeatGroupSingle.qrItem
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const valueFromPropsQRItems = valueFromProps
|
|
32
|
+
.map((repeatGroupSingle) => repeatGroupSingle.qrItem)
|
|
33
|
+
.filter((qrItem): qrItem is QuestionnaireResponseItem => qrItem !== null);
|
|
34
|
+
|
|
35
|
+
const repeatGroupsQRItems = repeatGroups
|
|
36
|
+
.map((repeatGroupSingle) => repeatGroupSingle.qrItem)
|
|
37
|
+
.filter((qrItem): qrItem is QuestionnaireResponseItem => qrItem !== null);
|
|
34
38
|
|
|
35
39
|
if (!_isEqual(valueFromPropsQRItems, repeatGroupsQRItems)) {
|
|
36
40
|
setRepeatGroups(valueFromProps);
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { Questionnaire } from 'fhir/r4';
|
|
19
|
+
|
|
20
|
+
export const qMyPatient: Questionnaire = {
|
|
21
|
+
resourceType: 'Questionnaire',
|
|
22
|
+
id: 'canshare-myPatient1',
|
|
23
|
+
meta: {
|
|
24
|
+
versionId: '9',
|
|
25
|
+
lastUpdated: '2024-09-18T07:23:35.7317908+00:00'
|
|
26
|
+
},
|
|
27
|
+
extension: [
|
|
28
|
+
{
|
|
29
|
+
extension: [
|
|
30
|
+
{
|
|
31
|
+
url: 'name',
|
|
32
|
+
valueId: 'LaunchPatient'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
url: 'type',
|
|
36
|
+
valueCode: 'Patient'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
url: 'description',
|
|
40
|
+
valueString: 'The patient that is to be used to pre-populate the form'
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext'
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
extension: [
|
|
47
|
+
{
|
|
48
|
+
url: 'name',
|
|
49
|
+
valueId: 'LaunchPractitioner'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
url: 'type',
|
|
53
|
+
valueCode: 'Practitioner'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
url: 'description',
|
|
57
|
+
valueString: 'The practitioner that is to be used to pre-populate the form'
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-launchContext'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
url: 'http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-questionnaire-itemExtractionContext',
|
|
64
|
+
valueCode: 'Patient'
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
url: 'http://canshare.co.nz/questionnaire/myPatient1',
|
|
68
|
+
name: 'myPatient1',
|
|
69
|
+
status: 'active',
|
|
70
|
+
publisher: 'DEMO: David Hay',
|
|
71
|
+
useContext: [
|
|
72
|
+
{
|
|
73
|
+
code: {
|
|
74
|
+
system: 'http://terminology.hl7.org/CodeSystem/usage-context-type',
|
|
75
|
+
code: 'user',
|
|
76
|
+
display: 'User Type'
|
|
77
|
+
},
|
|
78
|
+
valueCodeableConcept: {
|
|
79
|
+
coding: [
|
|
80
|
+
{
|
|
81
|
+
code: 'extract',
|
|
82
|
+
display: 'Demo Extract'
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
item: [
|
|
89
|
+
{
|
|
90
|
+
linkId: 'myPatient1',
|
|
91
|
+
text: 'myPatient1',
|
|
92
|
+
type: 'group',
|
|
93
|
+
item: [
|
|
94
|
+
{
|
|
95
|
+
linkId: 'myPatient1.name',
|
|
96
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name',
|
|
97
|
+
text: 'name *',
|
|
98
|
+
type: 'group',
|
|
99
|
+
repeats: true,
|
|
100
|
+
item: [
|
|
101
|
+
{
|
|
102
|
+
linkId: 'myPatient1.name.first',
|
|
103
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name.given',
|
|
104
|
+
text: 'firstName *',
|
|
105
|
+
type: 'string',
|
|
106
|
+
repeats: true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
linkId: 'myPatient1.name.lastName',
|
|
110
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.name.family',
|
|
111
|
+
text: 'lastName',
|
|
112
|
+
type: 'string'
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
linkId: 'myPatient1.hair',
|
|
118
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension',
|
|
119
|
+
text: 'hair',
|
|
120
|
+
type: 'group',
|
|
121
|
+
item: [
|
|
122
|
+
{
|
|
123
|
+
linkId: 'myPatient1.hair.colour',
|
|
124
|
+
definition:
|
|
125
|
+
'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.valueString',
|
|
126
|
+
text: 'colour',
|
|
127
|
+
type: 'string'
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
linkId: 'myPatient1.hair.url',
|
|
131
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.url',
|
|
132
|
+
text: 'url',
|
|
133
|
+
type: 'string'
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
linkId: 'myPatient1.religion',
|
|
139
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension',
|
|
140
|
+
text: 'religion',
|
|
141
|
+
type: 'group',
|
|
142
|
+
item: [
|
|
143
|
+
{
|
|
144
|
+
linkId: 'myPatient1.religion.brand',
|
|
145
|
+
definition:
|
|
146
|
+
'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.valueString',
|
|
147
|
+
text: 'brand',
|
|
148
|
+
type: 'string'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
linkId: 'myPatient1.religion.url1',
|
|
152
|
+
definition: 'http://hl7.org/fhir/StructureDefinition/Patient#Patient.extension.url',
|
|
153
|
+
text: 'url',
|
|
154
|
+
type: 'string'
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
};
|
|
@@ -44,7 +44,7 @@ function BuildFormButtonForStorybook(props: BuildFormButtonProps) {
|
|
|
44
44
|
<Box display="flex" mb={0.5} alignItems="center" columnGap={3}>
|
|
45
45
|
<Tooltip title="Build form with questionnaire response" placement="right">
|
|
46
46
|
<IconButton onClick={handleBuildForm} size="small" color="primary">
|
|
47
|
-
<Iconify icon="ph:hammer"
|
|
47
|
+
<Iconify icon="ph:hammer" />
|
|
48
48
|
</IconButton>
|
|
49
49
|
</Tooltip>
|
|
50
50
|
</Box>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 Commonwealth Scientific and Industrial Research
|
|
3
|
+
* Organisation (CSIRO) ABN 41 687 119 230.
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
import React from 'react';
|
|
20
|
+
import type { Questionnaire, QuestionnaireResponse } from 'fhir/r4';
|
|
21
|
+
import { Box, IconButton, Tooltip } from '@mui/material';
|
|
22
|
+
import ContentCutIcon from '@mui/icons-material/ContentCut';
|
|
23
|
+
import { useQuestionnaireResponseStore } from '../../stores';
|
|
24
|
+
import { removeInternalIdsFromResponse } from '../../utils/manageForm';
|
|
25
|
+
|
|
26
|
+
interface IdRemoverButtonProps {
|
|
27
|
+
questionnaire: Questionnaire;
|
|
28
|
+
questionnaireResponse: QuestionnaireResponse;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function IdRemoverButtonForStorybook(props: IdRemoverButtonProps) {
|
|
32
|
+
const { questionnaire, questionnaireResponse } = props;
|
|
33
|
+
|
|
34
|
+
const updateResponse = useQuestionnaireResponseStore.use.updateResponse();
|
|
35
|
+
|
|
36
|
+
async function handleRemoveIds() {
|
|
37
|
+
updateResponse(removeInternalIdsFromResponse(questionnaire, questionnaireResponse));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<Box display="flex" mb={0.5} alignItems="center" columnGap={3}>
|
|
42
|
+
<Tooltip title="Remove IDs from questionnaire response" placement="right">
|
|
43
|
+
<IconButton onClick={handleRemoveIds} size="small" color="primary">
|
|
44
|
+
<ContentCutIcon />
|
|
45
|
+
</IconButton>
|
|
46
|
+
</Tooltip>
|
|
47
|
+
</Box>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default IdRemoverButtonForStorybook;
|