diff --git a/src/api/types.ts b/src/api/types.ts index 42f2efb02..de64f8471 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -163,6 +163,9 @@ export interface OHRIFormQuestionOptions { }; isDateTime?: { labelTrue: boolean; labelFalse: boolean }; usePreviousValueDisabled?: boolean; + allowedFileTypes?: Array; + allowMultiple?: boolean; //Allow Single File Attachments and Multiple file attachments + datasource?: { id: string; config?: Record }; datasource?: { name: string; config?: Record }; isSearchable?: boolean; } @@ -184,7 +187,8 @@ export type RenderType = | 'encounter-location' | 'textarea' | 'toggle' - | 'fixed-value'; + | 'fixed-value' + | 'file'; //allow the form engine to recognize and handle the new file component export interface PostSubmissionAction { applyAction( diff --git a/src/components/inputs/file/file.component.tsx b/src/components/inputs/file/file.component.tsx new file mode 100644 index 000000000..3580f3975 --- /dev/null +++ b/src/components/inputs/file/file.component.tsx @@ -0,0 +1,56 @@ +import React, { useState } from 'react'; +import { FileUploader } from '@carbon/react'; +import { useTranslation } from 'react-i18next'; +import { OHRIFormFieldProps } from '../../../api/types'; +import { useField } from 'formik'; +import { OHRIFormContext } from '../../../ohri-form-context'; + +interface FileProps extends OHRIFormFieldProps {} + +const File: React.FC = ({ question, onChange, handler }) => { + const { t } = useTranslation(); + const [field, meta] = useField(question.id); + const { setFieldValue } = React.useContext(OHRIFormContext); + + const labelDescription = question.questionOptions.allowedFileTypes + ? t('fileUploadDescription', `Upload one of the following file types: ${question.questionOptions.allowedFileTypes}`) + : t('fileUploadDescriptionAny', 'Upload any file type'); + + const [selectedFiles, setSelectedFiles] = useState([]); // Add state for selected files + + const handleFileChange = (event) => { + const newSelectedFiles = Array.from(event.target.files); + setSelectedFiles(newSelectedFiles); + setFieldValue(question.id, newSelectedFiles); // Update form field value + }; + + return ( +
+ + + {/* Display previews of selected files */} + {selectedFiles.length > 0 && ( +
+ {selectedFiles.map((file, index) => ( +
+

{file.name}

+ {file.type.includes('image') ? Preview : File Icon} +
+ ))} +
+ )} +
+ ); +}; + +export default File; diff --git a/src/registry/registry.ts b/src/registry/registry.ts index a3c56d619..1d3c97b50 100644 --- a/src/registry/registry.ts +++ b/src/registry/registry.ts @@ -1,6 +1,15 @@ import { DataSource, FieldValidator, OHRIFormFieldProps, PostSubmissionAction, SubmissionHandler } from '../api/types'; import { getGlobalStore } from '@openmrs/esm-framework'; import { OHRIFormsStore } from '../constants'; +import OHRIExtensionParcel from '../components/extension/ohri-extension-parcel.component'; +import { EncounterDatetimeHandler } from '../submission-handlers/encounterDatetimeHandler'; +import File from '../components/inputs/file/file.component'; +import { UISelectExtended } from '../components/inputs/ui-select-extended/ui-select-extended'; + + +export interface RegistryItem { + id: string; + component: any; import { inbuiltControls } from './inbuilt-components/inbuiltControls'; import { inbuiltFieldSubmissionHandlers } from './inbuilt-components/inbuiltFieldSubmissionHandlers'; import { inbuiltValidators } from './inbuilt-components/inbuiltValidators'; @@ -32,6 +41,167 @@ export interface FieldSubmissionHandlerRegistration extends ComponentRegistratio } export interface FormsRegistryStoreState { + customControls: Array; + postSubmissionActions: Array; +} + +export const baseFieldComponents: Array = [ + { + id: 'OHRIText', + loadControl: () => Promise.resolve({ default: OHRIText }), + type: 'text', + alias: '', + }, + { + id: 'OHRIRadio', + loadControl: () => Promise.resolve({ default: OHRIRadio }), + type: 'radio', + alias: '', + }, + { + id: 'OHRIDate', + loadControl: () => Promise.resolve({ default: OHRIDate }), + type: 'date', + alias: '', + }, + { + id: 'OHRINumber', + loadControl: () => Promise.resolve({ default: OHRINumber }), + type: 'number', + alias: 'numeric', + }, + { + id: 'OHRIMultiSelect', + loadControl: () => Promise.resolve({ default: OHRIMultiSelect }), + type: 'checkbox', + alias: 'multiCheckbox', + }, + { + id: 'OHRIContentSwitcher', + loadControl: () => Promise.resolve({ default: OHRIContentSwitcher }), + type: 'content-switcher', + alias: '', + }, + { + id: 'OHRIEncounterLocationPicker', + loadControl: () => Promise.resolve({ default: OHRIEncounterLocationPicker }), + type: 'encounter-location', + alias: '', + }, + { + id: 'UISelectExtended', + loadControl: () => Promise.resolve({ default: UISelectExtended }), + type: 'ui-select-extended', + }, + { + id: 'OHRIDropdown', + loadControl: () => Promise.resolve({ default: OHRIDropdown }), + type: 'select', + alias: '', + }, + { + id: 'OHRITextArea', + loadControl: () => Promise.resolve({ default: OHRITextArea }), + type: 'textarea', + alias: '', + }, + { + id: 'OHRIToggle', + loadControl: () => Promise.resolve({ default: OHRIToggle }), + type: 'toggle', + alias: '', + }, + { + id: 'OHRIObsGroup', + loadControl: () => Promise.resolve({ default: OHRIObsGroup }), + type: 'group', + alias: '', + }, + { + id: 'OHRIRepeat', + loadControl: () => Promise.resolve({ default: OHRIRepeat }), + type: 'repeating', + alias: '', + }, + { + id: 'OHRIFixedValue', + loadControl: () => Promise.resolve({ default: OHRIFixedValue }), + type: 'fixed-value', + alias: '', + }, + { + id: 'OHRIMarkdown', + loadControl: () => Promise.resolve({ default: OHRIMarkdown }), + type: 'markdown', + alias: '', + }, + { + id: 'OHRIExtensionParcel', + loadControl: () => Promise.resolve({ default: OHRIExtensionParcel }), + type: 'extension-widget', + alias: '', + }, + { + id: 'OHRIDateTime', + loadControl: () => Promise.resolve({ default: OHRIDate }), + type: 'datetime', + alias: '', + }, + { + id: 'file', + loadControl: () => Promise.resolve({ default: File }), + type: 'file', + alias: '', + }, +]; + +const baseHandlers: Array = [ + { + id: 'ObsSubmissionHandler', + component: ObsSubmissionHandler, + type: 'obs', + }, + { + id: 'ObsGroupHandler', + component: ObsSubmissionHandler, + type: 'obsGroup', + }, + { + id: 'EncounterLocationSubmissionHandler', + component: EncounterLocationSubmissionHandler, + type: 'encounterLocation', + }, + { + id: 'EncounterDatetimeHandler', + component: EncounterDatetimeHandler, + type: 'encounterDatetime', + }, +]; + +const fieldValidators: Array = [ + { + id: 'OHRIBaseValidator', + component: OHRIFieldValidator, + }, + { + id: 'date', + component: OHRIDateValidator, + }, + { + id: 'js_expression', + component: OHRIJSExpressionValidator, + }, +]; + +const dataSources: Array = []; + +export const getFieldComponent = renderType => { + let lazy = baseFieldComponents.find(item => item.type == renderType || item?.alias == renderType)?.loadControl; + if (!lazy) { + lazy = getOHRIFormsStore().customControls.find(item => item.type == renderType || item?.alias == renderType) + ?.loadControl; + } + return lazy?.(); controls: CustomControlRegistration[]; fieldValidators: ComponentRegistration[]; fieldSubmissionHandlers: FieldSubmissionHandlerRegistration[];