Skip to content

Commit

Permalink
Add concept answer added in edit
Browse files Browse the repository at this point in the history
  • Loading branch information
Willie-theBeastMutua committed Oct 2, 2024
1 parent 2f15f38 commit 951e36f
Show file tree
Hide file tree
Showing 6 changed files with 5,772 additions and 5,894 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"browser": "dist/openmrs-esm-form-builder-app.js",
"main": "src/index.ts",
"source": true,
"resolutions": {
"cheerio": "1.0.0-rc.10"
},
"scripts": {
"start": "openmrs develop",
"serve": "webpack serve --mode=development",
Expand Down Expand Up @@ -48,7 +51,9 @@
"dependencies": {
"@carbon/react": "^1.47.0",
"@openmrs/esm-form-engine-lib": "next",
"@openmrs/openmrs-form-engine-lib": "^2.1.0",
"ajv": "^8.17.1",
"cheerio": "^1.0.0",
"dotenv": "^16.4.5",
"file-loader": "^6.2.0",
"fuzzy": "^0.1.3",
Expand Down
114 changes: 47 additions & 67 deletions src/components/interactive-builder/add-question.modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import {
Form,
FormGroup,
FormLabel,
Layer,
InlineLoading,
InlineNotification,
IconButton,
Layer,
ModalBody,
ModalFooter,
ModalHeader,
Expand All @@ -20,15 +20,15 @@ import {
Search,
Select,
SelectItem,
SelectSkeleton,
Stack,
Tag,
TextInput,
Tile,
SelectSkeleton,
} from '@carbon/react';
import { ArrowUpRight, Add, TrashCan } from '@carbon/react/icons';
import { showSnackbar, useConfig, useDebounce } from '@openmrs/esm-framework';
import type { ProgramState, RenderType } from '@openmrs/esm-form-engine-lib';
import type { ProgramState, RenderType } from '@openmrs/openmrs-form-engine-lib';

import type { ConfigObject } from '../../config-schema';
import type {
Expand All @@ -41,14 +41,12 @@ import type {
QuestionType,
Program,
ProgramWorkflow,
DatePickerType,
DatePickerTypeOption,
} from '../../types';
import { useConceptLookup } from '../../hooks/useConceptLookup';
import { usePatientIdentifierTypes } from '../../hooks/usePatientIdentifierTypes';
import { usePersonAttributeTypes } from '../../hooks/usePersonAttributeTypes';
import { useProgramWorkStates, usePrograms } from '../../hooks/useProgramStates';
import styles from './question-modal.scss';
import { useProgramWorkStates, usePrograms } from '../../hooks/useProgramStates';

interface AddQuestionModalProps {
closeModal: () => void;
Expand All @@ -60,6 +58,14 @@ interface AddQuestionModalProps {
sectionIndex: number;
}

const DatePickerType = {
both: 'both',
calendar: 'calendar',
timer: 'timer',
} as const;

type DatePickerTypeValue = (typeof DatePickerType)[keyof typeof DatePickerType];

interface Item {
text: string;
}
Expand All @@ -74,21 +80,6 @@ interface RequiredLabelProps {
t: TFunction;
}

export const getDatePickerType = (concept: Concept): DatePickerType | null => {
const conceptDataType = concept.datatype.name;
switch (conceptDataType) {
case 'Datetime':
return 'both';
case 'Date':
return 'calendar';
case 'Time':
return 'timer';
default:
console.warn(`Unsupported datatype for date fields: ${conceptDataType}`);
return null;
}
};

const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
closeModal,
onSchemaChange,
Expand All @@ -105,7 +96,10 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
const debouncedConceptToLookup = useDebounce(conceptToLookup);
const [selectedConcept, setSelectedConcept] = useState<Concept | null>(null);
const debouncedAnsConceptToLookup = useDebounce(conceptAnsToLookup);
const [datePickerType, setDatePickerType] = useState<DatePickerType>('both');

const [datePickerType, setDatePickerType] = useState<(typeof DatePickerType)[DatePickerTypeValue]>(
DatePickerType.both,
);
const [renderingType, setRenderingType] = useState<RenderType | null>(null);
const [isQuestionRequired, setIsQuestionRequired] = useState(false);
const [max, setMax] = useState('');
Expand Down Expand Up @@ -162,20 +156,11 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
programState: ['select'],
};

// Maps the data type of a concept to a date picker type.
const datePickerTypeOptions: Record<string, Array<DatePickerTypeOption>> = {
datetime: [{ value: 'both', label: t('calendarAndTimer', 'Calendar and timer'), defaultChecked: true }],
date: [{ value: 'calendar', label: t('calendarOnly', 'Calendar only'), defaultChecked: false }],
time: [{ value: 'timer', label: t('timerOnly', 'Timer only'), defaultChecked: false }],
};

const handleConceptChange = (event: React.ChangeEvent<HTMLInputElement>) => setConceptToLookup(event.target.value);
const handleAnsConceptChange = (event: React.ChangeEvent<HTMLInputElement>) =>
setConceptAnsToLookup(event.target.value);

const handleConceptSelect = (concept: Concept) => {
const updatedDatePickerType = getDatePickerType(concept);
if (updatedDatePickerType) setDatePickerType(updatedDatePickerType);
setConceptToLookup('');
setSelectedConcept(concept);
setAnswers(
Expand All @@ -184,6 +169,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
label: answer?.display,
})),
);

setConceptMappings(
concept?.mappings?.map((conceptMapping) => {
const data = conceptMapping.display.split(': ');
Expand All @@ -209,6 +195,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
const newAnswer = { id: concept.uuid, text: concept.display };
setaddedAnswers((prevAnswers) => [...prevAnswers, newAnswer]);
};

const handlePersonAttributeTypeChange = ({ selectedItem }: { selectedItem: PersonAttributeType }) => {
setSelectedPersonAttributeType(selectedItem);
};
Expand Down Expand Up @@ -244,11 +231,10 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
type: questionType,
required: isQuestionRequired,
id: questionId ?? computedQuestionId,
...((renderingType === 'date' || renderingType === 'datetime') &&
datePickerType && { datePickerFormat: datePickerType }),
...(questionType === 'encounterDatetime' && { datePickerFormat: datePickerType }),
questionOptions: {
rendering: renderingType,
...(selectedConcept && { concept: selectedConcept?.uuid }),
concept: selectedConcept?.uuid ? selectedConcept?.uuid : '',
...(conceptMappings.length && { conceptMappings }),
...(selectedAnswers.length && {
answers: selectedAnswers.map((answer) => ({
Expand All @@ -269,7 +255,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
selectedConcept?.allowDecimal === false && { disallowDecimals: true }),
...(questionType === 'programState' && {
answers: selectedProgramState.map((answer) => ({
value: answer.uuid,
value: answer.concept.uuid,
label: answer.concept.display,
})),
programUuid: selectedProgram.uuid,
Expand Down Expand Up @@ -341,11 +327,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({

return (
<>
<ModalHeader
className={styles.modalHeader}
title={t('createNewQuestion', 'Create a new question')}
closeModal={closeModal}
/>
<ModalHeader title={t('createNewQuestion', 'Create a new question')} closeModal={closeModal} />
<Form className={styles.form} onSubmit={(event: React.SyntheticEvent) => event.preventDefault()}>
<ModalBody hasScrollingContent>
<FormGroup legendText={''}>
Expand Down Expand Up @@ -553,7 +535,6 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
id="conceptLookup"
onClear={() => {
setSelectedConcept(null);
setDatePickerType('both');
setAnswers([]);
setConceptMappings([]);
}}
Expand Down Expand Up @@ -691,6 +672,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
))}
</div>
) : null}

{selectedConcept && answers?.length ? (
<div>
<Button kind="tertiary" onClick={showAddQuestion} iconDescription="Add" size="sm">
Expand Down Expand Up @@ -818,7 +800,7 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
legendText={t('addObsCommentTextBox', 'Add obs comment text box')}
>
<RadioButton
id="obsCommentYes"
id="obsCommentYes "
defaultChecked={true}
labelText={t('yes', 'Yes')}
onClick={() => setAddObsComment(true)}
Expand Down Expand Up @@ -857,35 +839,33 @@ const AddQuestionModal: React.FC<AddQuestionModalProps> = ({
</>
) : null}

{renderingType === 'date' || renderingType === 'datetime' ? (
{questionType === 'encounterDatetime' ? (
<RadioButtonGroup
defaultSelected="both"
name="datePickerType"
legendText={t('datePickerType', 'The type of date picker to show ')}
>
{/** Filters out the date picker types based on the selected concept's data type.
If no concept is selected, all date picker types are shown.
*/}
{selectedConcept && selectedConcept.datatype
? datePickerTypeOptions[selectedConcept.datatype.name.toLowerCase()].map((type) => (
<RadioButton
id={type.value}
labelText={type.label}
onClick={() => setDatePickerType(type.value)}
checked={datePickerType === type.value}
value={type.value}
/>
))
: Object.values(datePickerTypeOptions)
.flat()
.map((type) => (
<RadioButton
id={type.value}
checked={datePickerType === type.value}
labelText={type.label}
onClick={() => setDatePickerType(type.value)}
value={type.value}
/>
))}
<RadioButton
id="both"
defaultChecked={true}
labelText={t('calendarAndTimer', 'Calendar and timer')}
onClick={() => setDatePickerType(DatePickerType.both)}
value="both"
/>
<RadioButton
id="calendar"
defaultChecked={false}
labelText={t('calendarOnly', 'Calendar only')}
onClick={() => setDatePickerType(DatePickerType.calendar)}
value="calendar"
/>
<RadioButton
id="timer"
defaultChecked={false}
labelText={t('timerOnly', 'Timer only')}
onClick={() => setDatePickerType(DatePickerType.timer)}
value="timer"
/>
</RadioButtonGroup>
) : null}

Expand Down
Loading

0 comments on commit 951e36f

Please sign in to comment.