diff --git a/src/assets/possibleValues.json b/src/assets/possibleValues.json new file mode 100644 index 00000000..740a1a52 --- /dev/null +++ b/src/assets/possibleValues.json @@ -0,0 +1,97 @@ +[ + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--0", + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Fully active, able to carry on all pre-disease performance without restriction." + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "0" + } + ] + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--1", + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Restricted in physically strenuous activity but ambulatory and able to carry out work of a light or sedentary nature, e.g. light house work, office work." + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "1" + } + ] + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--2", + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Ambulatory and capable of all selfcare but unable to carry out any work activities. Up and about more than 50% of waking hours." + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "2" + } + ] + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--3", + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Capable of only limited selfcare, confined to bed or chair more than 50% of waking hours." + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "3" + } + ] + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--4", + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Completely disabled. Cannot carry on selfcare. Totally confined to bed or chair." + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "4" + } + ] + }, + { + "@id": "http://vfn.cz/ontologies/study-model/answer-value--na", + "http://onto.fel.cvut.cz/ontologies/form/has-preceding-value": [ + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--3" + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--0" + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--1" + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--4" + }, + { + "@id": "http://vfn.cz/ontologies/abrax-study-model/ecog-status--2" + } + ], + "http://www.w3.org/2000/01/rdf-schema#comment": [ + { + "@value": "Not available value" + } + ], + "http://www.w3.org/2000/01/rdf-schema#label": [ + { + "@value": "Not available" + } + ] + } +] diff --git a/src/components/record/Record.jsx b/src/components/record/Record.jsx index 81c2058b..9839ac9b 100644 --- a/src/components/record/Record.jsx +++ b/src/components/record/Record.jsx @@ -38,6 +38,9 @@ class Record extends React.Component { _onChange = (e) => { const change = {}; change[e.target.name] = e.target.value; + if (e.formTemplateVersion) { + change.formTemplateVersion = e.formTemplateVersion; + } this.props.handlers.onChange(change); }; @@ -121,6 +124,7 @@ class Record extends React.Component { if (!record?.formTemplate) { if (formTemplate) { record.formTemplate = formTemplate; + record.formTemplateVersion = this._getFormTemplateVersion(); } } @@ -297,6 +301,20 @@ class Record extends React.Component { ); } + _getFormTemplateVersion() { + const { formTemplatesLoaded, record } = this.props; + const formTemplate = this.props.formTemplate || record?.formTemplate; + try { + if (formTemplate && formTemplatesLoaded) { + return formTemplatesLoaded.formTemplates.find((t) => t["@id"] === formTemplate)[ + "http://purl.org/dc/terms/hasVersion" + ][0]["@id"]; + } + } catch (e) { + return ""; + } + } + _getFormTemplateName() { const { formTemplatesLoaded, record, intl } = this.props; const formTemplate = this.props.formTemplate || record?.formTemplate; diff --git a/src/components/record/RecordController.jsx b/src/components/record/RecordController.jsx index 9ef2b463..e8e86e12 100644 --- a/src/components/record/RecordController.jsx +++ b/src/components/record/RecordController.jsx @@ -121,6 +121,9 @@ class RecordController extends React.Component { _onChange = (change) => { const update = { ...this.state.record, ...change }; + if (change.formTemplateVersion) { + update.formTemplateVersion = change.formTemplateVersion; + } if (RecordValidator.isComplete(update)) { update.state.recordComplete(); } else { diff --git a/src/components/record/RecordForm.jsx b/src/components/record/RecordForm.jsx index b33f6056..e13af1f3 100644 --- a/src/components/record/RecordForm.jsx +++ b/src/components/record/RecordForm.jsx @@ -10,6 +10,7 @@ import { axiosBackend } from "../../actions"; import { API_URL } from "../../../config"; import * as Logger from "../../utils/Logger"; import * as I18nStore from "../../stores/I18nStore"; +import possibleValues from "../../assets/possibleValues.json"; // TODO enable s-forms-components // import SmartComponents from "s-forms-components"; @@ -27,6 +28,9 @@ class RecordForm extends React.Component { this.form = this.props.form; this.updateForm = this.props.updateForm; this.refForm = React.createRef(); + this.state = { + validTypeaheadEndpoint: true, + }; } componentDidMount() { @@ -65,8 +69,23 @@ class RecordForm extends React.Component { fetchTypeAheadValues = async (query) => { const FORM_GEN_POSSIBLE_VALUES_URL = `${API_URL}/rest/formGen/possibleValues`; - const result = await axiosBackend.get(`${FORM_GEN_POSSIBLE_VALUES_URL}?query=${encodeURIComponent(query)}`); - return result.data; + if (this.state.validTypeaheadEndpoint) { + try { + const result = await axiosBackend.get(`${FORM_GEN_POSSIBLE_VALUES_URL}?query=${encodeURIComponent(query)}`); + return result.data; + } catch (e) { + this.setState({ validTypeaheadEndpoint: false }, () => { + // Fallback logic after state update + return new Promise((resolve) => { + setTimeout(() => resolve(possibleValues), 1000); + }); + }); + } + } else { + return new Promise((resolve) => { + setTimeout(() => resolve(possibleValues), 1000); + }); + } }; _getUsersOptions() { diff --git a/src/components/record/RecordProvenance.jsx b/src/components/record/RecordProvenance.jsx index 87e79443..f2d2fc3e 100644 --- a/src/components/record/RecordProvenance.jsx +++ b/src/components/record/RecordProvenance.jsx @@ -12,10 +12,15 @@ const RecordProvenance = (props) => { } const author = record.author ? record.author.firstName + " " + record.author.lastName : "", created = formatDate(new Date(record.dateCreated)); + + const version = record.formTemplateVersion?.substring(record.formTemplateVersion?.lastIndexOf("/") + 1); + if (!record.lastModified) { return (
{author} }} /> +
+ {version} }} />
); } @@ -28,6 +33,8 @@ const RecordProvenance = (props) => { {author} }} />
{lastEditor} }} /> +
+ {version} }} /> ); }; diff --git a/src/components/record/RequiredAttributes.jsx b/src/components/record/RequiredAttributes.jsx index 8eb97a90..cca99fbc 100644 --- a/src/components/record/RequiredAttributes.jsx +++ b/src/components/record/RequiredAttributes.jsx @@ -43,7 +43,14 @@ class RequiredAttributes extends React.Component { name="formTemplate" value={record.formTemplate || formTemplate} label={this.i18n("records.form-template") + "*"} - onChange={this.props.onChange} + onChange={(e, selectedOption) => { + // selectedOption will contain the full template data + const changes = { + ...e, + formTemplateVersion: selectedOption.version, + }; + this.props.onChange(changes); + }} possibleValuesEndpoint={possibleValuesEndpoint} /> diff --git a/src/components/record/TypeaheadAnswer.jsx b/src/components/record/TypeaheadAnswer.jsx index 602f591e..875efda0 100644 --- a/src/components/record/TypeaheadAnswer.jsx +++ b/src/components/record/TypeaheadAnswer.jsx @@ -21,7 +21,20 @@ export const processTypeaheadOptions = (options, intl) => { // sort by property JsonLdObjectUtils.orderPreservingToplogicalSort(options, Constants.HAS_PRECEDING_VALUE); - return JsonLdUtils.processTypeaheadOptions(options, intl); + let processedOptions = JsonLdUtils.processTypeaheadOptions(options, intl); + const versionPredicate = "http://purl.org/dc/terms/hasVersion"; + processedOptions = processedOptions.map((option, index) => { + const version = options[index][versionPredicate]; + if (version) { + return { + ...option, + version: options[index][versionPredicate][0]["@id"], + }; + } + + return option; + }); + return processedOptions; }; const TypeaheadAnswer = (props) => { @@ -45,7 +58,7 @@ const TypeaheadAnswer = (props) => { if (option) { e.target.value = option.id; } - props.onChange(e); + props.onChange(e, option); }; return ( diff --git a/src/i18n/cs.js b/src/i18n/cs.js index 26a98e69..8593d895 100644 --- a/src/i18n/cs.js +++ b/src/i18n/cs.js @@ -186,6 +186,7 @@ export default { "record.institution": "Záznam vyplněn v", "record.created-by-msg": "Vytvořil(a) {name} {date}.", "record.last-edited-msg": "Naposledy upravil(a) {name} {date}.", + "record.form-template-version": "Původní verze šablony: {version}", "record.save-success": "Záznam úspěšně uložen.", "record.complete-success": "Formulář byl úspěšně dokončen.", "record.reject-success": "Formulář byl úspěšně zamítnut.", diff --git a/src/i18n/en.js b/src/i18n/en.js index 870467f4..1b6e5ddc 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -187,6 +187,7 @@ export default { "record.institution": "Form created at", "record.created-by-msg": "Created {date} by {name}.", "record.last-edited-msg": "Last modified {date} by {name}.", + "record.form-template-version": "Original form template version: {version}", "record.save-success": "Form successfully saved.", "record.complete-success": "Form successfully completed.", "record.reject-success": "Form successfully rejected.", diff --git a/src/utils/EntityFactory.js b/src/utils/EntityFactory.js index 50cb95f7..8f0be46b 100644 --- a/src/utils/EntityFactory.js +++ b/src/utils/EntityFactory.js @@ -29,6 +29,7 @@ export function initNewPatientRecord() { return { localName: "", formTemplate: "", + formTemplateVersion: "", complete: false, isNew: true, state: RecordState.createRecordState(), diff --git a/tests/__tests__/components/RecordProvenance.spec.jsx b/tests/__tests__/components/RecordProvenance.spec.jsx index fbd894cd..1f52dff4 100644 --- a/tests/__tests__/components/RecordProvenance.spec.jsx +++ b/tests/__tests__/components/RecordProvenance.spec.jsx @@ -55,7 +55,7 @@ describe("RequiredProvenance", function () { , ); const result = TestUtils.scryRenderedDOMComponentsWithTag(tree, "b"); - expect(result.length).toEqual(1); + expect(result.length).toEqual(2); }); it("renders info about date created and modified", function () { @@ -65,6 +65,6 @@ describe("RequiredProvenance", function () { , ); const result = TestUtils.scryRenderedDOMComponentsWithTag(tree, "b"); - expect(result.length).toEqual(2); + expect(result.length).toEqual(3); }); });