├── .editorconfig ├── .env ├── .eslintignore ├── .eslintrc ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── deploy-workflow.yaml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── config-overrides.js ├── deploy.yml ├── design ├── readme.md └── shr_fluxnotes_concept_v11.3_screens_components_v1.pdf ├── docs ├── FluxNotesManual_v0.1.docx ├── Structured Phrase Format Descriptions.pptx └── fluxArchitecture.vpp ├── package.json ├── public ├── DebraHernandez672.jpg ├── DebraHernandez672.png ├── EllaOrtiz111.png ├── JaneBradshaw146.png ├── ServerConfig.json ├── WEB-INF │ └── web.xml ├── WesWelker83.png ├── clinicalTrialEnrollmentSheet.pdf ├── clinicalTrialUnenrolledSheet.pdf ├── compass-logo.png ├── config.js ├── deceasedSheet.pdf ├── diseaseStatusSheet.pdf ├── fluxnotes_logo_b&w.png ├── fluxnotes_logo_color.png ├── icare_logo_b&w.png ├── icare_logo_color.png ├── icons │ ├── angled-pencil.svg │ ├── icon_arrow_left.svg │ ├── icon_view_left.png │ ├── icon_view_left.svg │ ├── icon_view_middle.png │ ├── icon_view_middle.svg │ ├── icon_view_right.png │ └── icon_view_right.svg ├── index.html ├── landing │ ├── fluxnotes_compass.gif │ ├── fluxnotes_full.gif │ ├── fluxnotes_gif_fullSize.gif │ ├── fluxnotes_poc.gif │ ├── grid_background.png │ ├── icon_efficacy_research.png │ ├── icon_efficacy_research.svg │ ├── icon_market_surveillance.png │ ├── icon_market_surveillance.svg │ ├── icon_provider_burden.png │ ├── icon_provider_burden.svg │ ├── icon_rare_diseases.png │ ├── icon_rare_diseases.svg │ ├── icon_regimen_analysis.png │ ├── icon_regimen_analysis.svg │ ├── icon_utilization.png │ ├── icon_utilization.svg │ └── img_fluxnotes_lite.jpg ├── logos │ ├── ASCO.png │ ├── Alliance.png │ ├── BWH.png │ ├── Browserstack-logo.png │ ├── DanaFarber.png │ ├── FluxNotesLogo.png │ ├── ICARE.png │ ├── MITRE.png │ ├── SHR.png │ ├── codex-Logo.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ └── mCODE-Logo.png ├── mapper.js ├── pathology-report.pdf ├── spa.jsp ├── stagingSheet.pdf └── toxicitySheet.pdf ├── scripts └── check-version.js ├── src ├── __test__ │ ├── TestPatient2V09.json │ ├── TestPatientV09.json │ ├── backend │ │ ├── config │ │ │ └── ConfigManager.test.js │ │ ├── dashboard │ │ │ └── DashboardManager.test.js │ │ ├── dataaccess │ │ │ ├── DataAccess.test.js │ │ │ └── McodeV09SmartOnFhirDataSource.test.js │ │ ├── lib │ │ │ ├── cql-execution │ │ │ │ └── CQLExecutionEngine.test.js │ │ │ ├── progression_lookup.test.js │ │ │ ├── staging.test.js │ │ │ ├── tnmstage_lookup.test.js │ │ │ └── toxicreaction_lookup.test.js │ │ ├── mcode │ │ │ ├── BarChart.test.js │ │ │ ├── OptionsCheckboxList.test.js │ │ │ ├── TreatmentOptionsActions.test.js │ │ │ ├── TreatmentOptionsOutcome.test.js │ │ │ ├── TreatmentOptionsReducers.test.js │ │ │ ├── mock-data │ │ │ │ ├── index.js │ │ │ │ ├── patients.json │ │ │ │ └── testoptions.json │ │ │ └── services │ │ │ │ ├── CLQOutcomesService.test.js │ │ │ │ ├── filter.json │ │ │ │ ├── processed.js │ │ │ │ ├── response.json │ │ │ │ └── rows.js │ │ ├── model │ │ │ ├── CodeableConceptUtils.test.js │ │ │ ├── actor │ │ │ │ └── FluxDeceased.disabled.js │ │ │ ├── lab │ │ │ │ └── FluxTest.disabled.js │ │ │ ├── medication │ │ │ │ └── FluxMedicationPrescription.disabled.js │ │ │ ├── oncology │ │ │ │ ├── FluxEstrogenReceptorStatus.disabled.js │ │ │ │ ├── FluxHistologicGrade.disabled.js │ │ │ │ ├── FluxHumanEpiduralGrowthFactorReceptor2Status.disabled.js │ │ │ │ ├── FluxProgesteroneReceptorStatus.disabled.js │ │ │ │ ├── FluxProgression.disabled.js │ │ │ │ ├── FluxTNMStage.disabled.js │ │ │ │ └── FluxTumorSize.disabled.js │ │ │ ├── procedure │ │ │ │ └── FluxProcedure.disabled.js │ │ │ └── research │ │ │ │ └── FluxStudy.disabled.js │ │ ├── noteparser │ │ │ ├── NoteParser.test.js │ │ │ └── NoteParserUtils.js │ │ ├── patient │ │ │ ├── FakeDataElement.js │ │ │ └── Patient.test.js │ │ ├── patientControl │ │ │ ├── FakePatient.js │ │ │ └── PatientControl.test.js │ │ └── views │ │ │ ├── AppManager.test.js │ │ │ ├── FullApp.test.js │ │ │ ├── Landing.test.js │ │ │ ├── LaunchPage.test.js │ │ │ └── SlimApp.test.js │ ├── testHelper.js │ └── ui │ │ ├── FullApp.js │ │ └── SlimApp.js ├── actions │ ├── mcode.js │ └── types.js ├── apps │ └── AppManager.jsx ├── clinicalTrials │ └── ClinicalTrialsList.jsx ├── components │ ├── LandingPage.jsx │ ├── LaunchPage.jsx │ └── WithTracker.jsx ├── config │ ├── ConfigManager.js │ └── ServiceManager.js ├── containers │ ├── App.jsx │ ├── CompassApp.jsx │ ├── FullApp.jsx │ ├── Pilot2MvpApp.jsx │ ├── PointOfCareApp.jsx │ ├── SlimApp.jsx │ ├── SmartCompassApp.jsx │ ├── SmartFullApp.jsx │ ├── SmartPilot2MvpApp.jsx │ └── WithSmartData.jsx ├── context │ ├── Context.jsx │ ├── ContextCalendar.jsx │ ├── ContextGetHelp.jsx │ ├── ContextGetHelp.scss │ ├── ContextItem.jsx │ ├── ContextItem.scss │ ├── ContextListOptions.jsx │ ├── ContextListOptions.scss │ ├── ContextManager.jsx │ ├── ContextOptions.jsx │ ├── ContextOptions.scss │ ├── ContextTray.jsx │ ├── ContextTray.scss │ ├── EditorPortal.jsx │ ├── EditorPortal.scss │ ├── PatientContext.jsx │ ├── PlaceholderViewModeContent.jsx │ ├── ShortcutSearch.jsx │ ├── ShortcutViewModeContent.jsx │ ├── ShortcutViewModeContent.scss │ ├── SnippetViewModeContent.jsx │ └── SnippetViewModeContent.scss ├── dashboard │ ├── ClinicianDashboard.css │ ├── ClinicianDashboard.jsx │ ├── CompassAppDashboard.jsx │ ├── CompassAppDashboard.scss │ ├── DashboardManager.jsx │ ├── NextPatientButton.jsx │ ├── NextPatientButton.scss │ ├── PointOfCareDashboard.jsx │ └── PointOfCareDashboard.scss ├── dataaccess │ ├── BreastMainTreatmentDebraV09.json │ ├── BreastMainTreatmentDiabetesHypertensionJaneV09.json │ ├── BreastMainTreatmentDiabetesHypertensionJaneV09_02.json │ ├── BreastMainTreatmentDiabetesHypertensionJaneV09_03.json │ ├── BreastMainTreatmentTry3EllaV09.json │ ├── DataAccess.js │ ├── FHIRApiDataSource.js │ ├── GenericSmartOnFhirDstu2DataSource.js │ ├── GistAdjuvantIhanosV09.json │ ├── HardCodedConvertedFHIRPatient.json │ ├── HardCodedConvertedFHIRPatientV09.json │ ├── HardCodedFHIRPatient.json │ ├── HardCodedMcodeV09DataSource.js │ ├── HardcodedTabletMcodeV09DataSource.js │ ├── IDataSource.js │ ├── McodeV05EntryMapper.js │ ├── McodeV09SmartOnFhirDataSource.js │ ├── NewPatientOnlyDataSource.js │ ├── RestApiDataSource.js │ ├── mappers │ │ ├── CernerSandboxMapper.js │ │ └── index.js │ ├── sample_curation_output.json │ └── utils │ │ └── fhir-entry-processor.js ├── elements │ ├── Button.js │ ├── ChoiceButton.js │ ├── MenuItem.js │ ├── SearchBar.jsx │ ├── SearchBar.scss │ └── Select.js ├── forms │ ├── ClinicalTrialEnrollmentForm.css │ ├── ClinicalTrialEnrollmentForm.jsx │ ├── ClinicalTrialUnenrolledForm.css │ ├── ClinicalTrialUnenrolledForm.jsx │ ├── DataCaptureForm.css │ ├── DataCaptureForm.jsx │ ├── DatePicker.css │ ├── DatePicker.jsx │ ├── DeceasedForm.css │ ├── DeceasedForm.jsx │ ├── FormList.css │ ├── FormList.jsx │ ├── FormSearch.css │ ├── FormSearch.jsx │ ├── LandingPageForm.css │ ├── LandingPageForm.jsx │ ├── MultiChoiceButton.jsx │ ├── MultiChoiceButton.scss │ ├── ProgressionForm.css │ ├── ProgressionForm.jsx │ ├── SingleChoiceButton.jsx │ ├── SingleChoiceButton.scss │ ├── StagingForm.css │ ├── StagingForm.jsx │ ├── ToxicityForm.css │ ├── ToxicityForm.jsx │ └── index.js ├── index.css ├── index.js ├── lib │ ├── FHIRMapper.js │ ├── MedicationInformationService.jsx │ ├── ValueSetManager.jsx │ ├── cancer_lookup.jsx │ ├── clinicaltrial_lookup.jsx │ ├── cql-execution │ │ ├── CQLExecutionEngine.js │ │ ├── README.md │ │ ├── codeservice │ │ │ ├── CodeService.js │ │ │ ├── README.md │ │ │ ├── valueSetDownloader.js │ │ │ └── vsac_cache │ │ │ │ └── valueset-db.json │ │ └── example │ │ │ ├── cql │ │ │ ├── BreastCancer.cql │ │ │ ├── BreastCancer.js │ │ │ ├── DiabeticFootExam.cql │ │ │ ├── DiabeticFootExam.js │ │ │ ├── DiabeticFootExam.json │ │ │ ├── PALLASEligibility.cql │ │ │ ├── PALLASEligibility.json │ │ │ ├── PatinaEligibility.cql │ │ │ ├── PatinaEligibility.json │ │ │ ├── age.cql │ │ │ ├── age.js │ │ │ ├── age.js.map │ │ │ ├── example.js │ │ │ ├── example.js.map │ │ │ ├── gender.cql │ │ │ └── gender.js │ │ │ └── patients │ │ │ ├── No_Foot_Exam.json │ │ │ ├── Old_Foot_Exam.json │ │ │ ├── PALLASPatient.json │ │ │ ├── PATINAPatient.json │ │ │ └── examplePatientSource.json │ ├── progression_lookup.jsx │ ├── react-minimap │ │ ├── components │ │ │ └── Child.js │ │ ├── index.js │ │ ├── react-minimap.js │ │ └── react-minimap.scss │ ├── receptor_lookup.jsx │ ├── slate-suggestions-dist │ │ ├── caret-position.js │ │ ├── constants.js │ │ ├── current-word.js │ │ ├── index.js │ │ ├── suggestion-item.js │ │ └── suggestion-portal.js │ ├── slate │ │ ├── Readme.md │ │ ├── components │ │ │ ├── Readme.md │ │ │ ├── content.js │ │ │ ├── editor.js │ │ │ ├── leaf.js │ │ │ ├── node.js │ │ │ ├── placeholder.js │ │ │ └── void.js │ │ ├── constants │ │ │ ├── Readme.md │ │ │ ├── environment.js │ │ │ ├── is-dev.js │ │ │ └── types.js │ │ ├── index.js │ │ ├── models │ │ │ ├── Readme.md │ │ │ ├── block.js │ │ │ ├── character.js │ │ │ ├── data.js │ │ │ ├── document.js │ │ │ ├── inline.js │ │ │ ├── mark.js │ │ │ ├── node.js │ │ │ ├── range.js │ │ │ ├── schema.js │ │ │ ├── selection.js │ │ │ ├── stack.js │ │ │ ├── state.js │ │ │ ├── text.js │ │ │ └── transform.js │ │ ├── plugins │ │ │ ├── Readme.md │ │ │ └── core.js │ │ ├── schemas │ │ │ ├── Readme.md │ │ │ └── core.js │ │ ├── serializers │ │ │ ├── Readme.md │ │ │ ├── base-64.js │ │ │ ├── html.js │ │ │ ├── plain.js │ │ │ └── raw.js │ │ ├── transforms │ │ │ ├── Readme.md │ │ │ ├── apply-operation.js │ │ │ ├── at-current-range.js │ │ │ ├── at-range.js │ │ │ ├── by-key.js │ │ │ ├── call.js │ │ │ ├── index.js │ │ │ ├── normalize.js │ │ │ ├── on-history.js │ │ │ ├── on-selection.js │ │ │ └── operations.js │ │ └── utils │ │ │ ├── Readme.md │ │ │ ├── extend-selection.js │ │ │ ├── find-closest-node.js │ │ │ ├── find-deepest-node.js │ │ │ ├── find-dom-node.js │ │ │ ├── generate-key.js │ │ │ ├── get-point.js │ │ │ ├── get-transfer-data.js │ │ │ ├── is-in-range.js │ │ │ ├── is-react-component.js │ │ │ ├── memoize.js │ │ │ ├── noop.js │ │ │ ├── normalize-node-and-offset.js │ │ │ ├── normalize.js │ │ │ ├── offset-key.js │ │ │ ├── scroll-to-selection.js │ │ │ ├── string.js │ │ │ └── warn.js │ ├── staging.jsx │ ├── tnmstage_lookup.jsx │ └── toxicreaction_lookup.jsx ├── loading │ ├── LoadingAnimation.jsx │ └── LoadingError.jsx ├── mcode-pilot │ ├── components │ │ ├── OptionsCheckboxList │ │ │ ├── OptionsCheckboxList.jsx │ │ │ └── OptionsCheckboxList.scss │ │ ├── OptionsCheckboxUnit │ │ │ └── OptionsCheckboxUnit.jsx │ │ ├── OptionsRangeSelector │ │ │ ├── OptionsRangeSelector.jsx │ │ │ └── OptionsRangeSelector.scss │ │ ├── SimilarPatientsSelector │ │ │ ├── SimilarPatientsSelector.jsx │ │ │ └── SimilarPatientsSelector.scss │ │ ├── TreatmentOptionsOutcomes │ │ │ ├── TreatmentOptionsOutcomes.jsx │ │ │ └── TreatmentOptionsOutcomes.scss │ │ ├── TreatmentOptionsOutcomesIcons │ │ │ ├── TreatmentOptionsOutcomesIcons.jsx │ │ │ └── TreatmentOptionsOutcomesIcons.scss │ │ ├── TreatmentOptionsOutcomesTable │ │ │ ├── CompareSelectedIcon.jsx │ │ │ ├── CompareUnselectedIcon.jsx │ │ │ ├── PersonIcon.jsx │ │ │ ├── TreatmentOptionsOutcomesHeaders.jsx │ │ │ ├── TreatmentOptionsOutcomesTable.jsx │ │ │ └── TreatmentOptionsOutcomesTable.scss │ │ ├── TreatmentOptionsSelector │ │ │ ├── TreatmentOptionsSelector.jsx │ │ │ └── TreatmentOptionsSelector.scss │ │ └── TreatmentsPopover │ │ │ ├── TreatmentsPopover.jsx │ │ │ └── TreatmentsPopover.scss │ ├── containers │ │ └── TreatmentOptionsVisualizer │ │ │ ├── TreatmentOptionsVisualizer.jsx │ │ │ └── TreatmentOptionsVisualizer.scss │ ├── mock-data │ │ ├── mock-data-update.js │ │ ├── mock-data.json │ │ └── transformedDataModel.json │ ├── services │ │ └── outcomes │ │ │ ├── CLQOutcomesService.js │ │ │ ├── IOutcomesService.js │ │ │ ├── StaticOutcomesService.js │ │ │ └── race_codes.js │ ├── utils │ │ ├── FilterOptions.js │ │ ├── arrayOperations.js │ │ ├── filterTreatmentData.js │ │ ├── numberWithCommas.js │ │ ├── recordToProps.js │ │ ├── serviceResultsProcessing.js │ │ └── sideEffects.js │ └── visualizations │ │ ├── BarChart │ │ ├── BarChart.jsx │ │ └── BarChart.scss │ │ ├── IconsChart │ │ ├── IconsChart.jsx │ │ └── IconsChart.scss │ │ └── TableLegend │ │ ├── TableLegend.jsx │ │ └── TableLegend.scss ├── model │ ├── ClassRegistry.js │ ├── CodeableConceptUtils.js │ ├── FluxObjectFactory.js │ ├── ObjectFactory.js │ ├── Reference.js │ ├── brca │ │ ├── AverageCEP17SignalsPerCell.js │ │ ├── AverageHER2SignalsPerCell.js │ │ ├── BrcaObjectFactory.js │ │ ├── BreastCancerCondition.js │ │ ├── BreastCancerHistologicGrade.js │ │ ├── BreastClinicalLymphNodeInvolvement.js │ │ ├── BreastLymphNodeBodyLocation.js │ │ ├── BreastLymphNodeInvolvement.js │ │ ├── BreastPathologicalLymphNodeInvolvement.js │ │ ├── BreastSite.js │ │ ├── BreastSpecimen.js │ │ ├── BreastTNMPathologicStageGroup.js │ │ ├── BreastTNMPrognosticClinicalStageGroup.js │ │ ├── ColdIschemiaTime.js │ │ ├── CompleteMembraneStainingPercent.js │ │ ├── DCISNuclearGrade.js │ │ ├── EstrogenReceptorAverageStainingIntensity.js │ │ ├── EstrogenReceptorNuclearPositivity.js │ │ ├── EstrogenReceptorStatus.js │ │ ├── ExtraCapsularExtensionOfNodalTumorStatus.js │ │ ├── HER2ReceptorStatus.js │ │ ├── HER2byFISH.js │ │ ├── HER2byIHC.js │ │ ├── HER2toCEP17Ratio.js │ │ ├── LymphNodeMobility.js │ │ ├── LymphNodeSamplingMethod.js │ │ ├── LymphNodeSize.js │ │ ├── MammaprintRecurrenceScore.js │ │ ├── MitoticCountScore.js │ │ ├── NuclearPleomorphismScore.js │ │ ├── NumberOfLymphNodesWithIsolatedTumorCells.js │ │ ├── NumberOfLymphNodesWithMacrometastases.js │ │ ├── NumberOfLymphNodesWithMicrometastases.js │ │ ├── NumberOfRegionalLymphNodes.js │ │ ├── NumberOfSentinelLymphNodes.js │ │ ├── OncotypeDxDCISRecurrenceScore.js │ │ ├── OncotypeDxInvasiveRecurrenceScore.js │ │ ├── ProgesteroneReceptorAverageStainingIntensity.js │ │ ├── ProgesteroneReceptorNuclearPositivity.js │ │ ├── ProgesteroneReceptorStatus.js │ │ ├── ProsignaRecurrenceScore.js │ │ ├── TotalNumberOfLymphNodesExamined.js │ │ └── TubuleFormationScore.js │ ├── fluxExtensions │ │ ├── BloodPressureFix.js │ │ ├── CancerReasonReferenceFix.js │ │ ├── CodeableConceptFix.js │ │ ├── CodingFix.js │ │ ├── DataValueFix.js │ │ ├── EntryFix.js │ │ ├── ExpectedPerformanceTimeFix.js │ │ ├── MedicationCodeOrReferenceFix.js │ │ ├── MedicationRequestFix.js │ │ ├── PrimaryCancerConditionFix.js │ │ ├── ReasonReferenceFix.js │ │ ├── RelatedCancerConditionFix.js │ │ ├── TNMClinicalStageGroupFix.js │ │ ├── TNMPathologicStageGroupFix.js │ │ └── TumorMarkerTestDataValueFix.js │ ├── fluxWrappers │ │ ├── base │ │ │ └── FluxEntry.js │ │ ├── core │ │ │ ├── FluxAdverseDrugReaction.js │ │ │ ├── FluxAllergyIntolerance.js │ │ │ ├── FluxBloodPressure.js │ │ │ ├── FluxBodyTemperature.js │ │ │ ├── FluxBodyWeight.js │ │ │ ├── FluxClinicalNote.js │ │ │ ├── FluxCondition.js │ │ │ ├── FluxCoreObjectFactory.js │ │ │ ├── FluxDiagnosticReport.js │ │ │ ├── FluxECOGPerformanceStatus.js │ │ │ ├── FluxEncounter.js │ │ │ ├── FluxHeartRate.js │ │ │ ├── FluxImagingProcedure.js │ │ │ ├── FluxKarnofskyPerformanceStatus.js │ │ │ ├── FluxMedicationBase.js │ │ │ ├── FluxMedicationRequest.js │ │ │ ├── FluxMedicationStatement.js │ │ │ ├── FluxObservation.js │ │ │ ├── FluxPatient.js │ │ │ ├── FluxPerson.js │ │ │ ├── FluxProcedure.js │ │ │ ├── FluxProcedureRequest.js │ │ │ ├── FluxQuestionnaireResponse.js │ │ │ ├── FluxQuestionnaireResponseItem.js │ │ │ ├── FluxReferralRequest.js │ │ │ ├── FluxResearchStudy.js │ │ │ └── FluxResearchSubject.js │ │ ├── onco │ │ │ └── core │ │ │ │ ├── FluxCancerCondition.js │ │ │ │ ├── FluxCancerDiseaseStatus.js │ │ │ │ ├── FluxCancerHistologicGrade.js │ │ │ │ ├── FluxCancerStageCategory.js │ │ │ │ ├── FluxEvidenceType.js │ │ │ │ ├── FluxGeneticMutationTestResult.js │ │ │ │ ├── FluxGenomicsReport.js │ │ │ │ ├── FluxOncocoreObjectFactory.js │ │ │ │ ├── FluxTNMClinicalDistantMetastasesCategory.js │ │ │ │ ├── FluxTNMClinicalPrimaryTumorCategory.js │ │ │ │ ├── FluxTNMClinicalRegionalNodesCategory.js │ │ │ │ ├── FluxTNMClinicalStageGroup.js │ │ │ │ ├── FluxTNMPathologicStageGroup.js │ │ │ │ ├── FluxTNMStageGroup.js │ │ │ │ └── FluxTumorMarkerTest.js │ │ ├── oncocore │ │ │ └── FluxCancerHistologicType.js │ │ └── tumor │ │ │ ├── FluxTumorDimensions.js │ │ │ ├── FluxTumorMargins.js │ │ │ └── FluxTumorObjectFactory.js │ ├── hpi-configuration.json │ ├── init.js │ ├── json-helper.js │ ├── onco │ │ └── core │ │ │ ├── CancerCondition.js │ │ │ ├── CancerDiseaseStatus.js │ │ │ ├── CancerHistologicGrade.js │ │ │ ├── CancerReasonReference.js │ │ │ ├── CancerRelatedRadiationProcedure.js │ │ │ ├── CancerRelatedSurgicalProcedure.js │ │ │ ├── CancerStageCategory.js │ │ │ ├── CancerStageGroup.js │ │ │ ├── CancerStagingSystem.js │ │ │ ├── EvidenceType.js │ │ │ ├── GeneStudied.js │ │ │ ├── GeneticMutationTestResult.js │ │ │ ├── GeneticVariantFound.js │ │ │ ├── GenomicSourceClass.js │ │ │ ├── GenomicsReport.js │ │ │ ├── HistologyMorphologyBehavior.js │ │ │ ├── IsPrimaryTumor.js │ │ │ ├── MutationTested.js │ │ │ ├── OncoCoreObjectFactory.js │ │ │ ├── PractitionerInformationSource.js │ │ │ ├── PrimaryCancerCondition.js │ │ │ ├── RegionStudied.js │ │ │ ├── RelatedCancerCondition.js │ │ │ ├── RelatedTumor.js │ │ │ ├── SecondaryCancerCondition.js │ │ │ ├── SizeOfGrossTumorBed.js │ │ │ ├── TNMClinicalDistantMetastasesCategory.js │ │ │ ├── TNMClinicalPrimaryTumorCategory.js │ │ │ ├── TNMClinicalRegionalNodesCategory.js │ │ │ ├── TNMClinicalStageGroup.js │ │ │ ├── TNMPathologicDistantMetastasesCategory.js │ │ │ ├── TNMPathologicPrimaryTumorCategory.js │ │ │ ├── TNMPathologicRegionalNodesCategory.js │ │ │ ├── TNMPathologicStageGroup.js │ │ │ ├── Tumor.js │ │ │ ├── TumorDimension2.js │ │ │ ├── TumorDimension3.js │ │ │ ├── TumorDimensions.js │ │ │ ├── TumorInvolvementAtSurgicalMargin.js │ │ │ ├── TumorLongestDimension.js │ │ │ ├── TumorMarkerTest.js │ │ │ ├── TumorMarkerTestDataValue.js │ │ │ ├── VariantDescription.js │ │ │ ├── VariantFoundDescription.js │ │ │ ├── VariantFoundHGVSName.js │ │ │ ├── VariantFoundIdentifier.js │ │ │ ├── VariantHGVSName.js │ │ │ └── VariantIdentifier.js │ ├── shr │ │ ├── base │ │ │ ├── Entry.js │ │ │ ├── EntryId.js │ │ │ ├── EntryType.js │ │ │ ├── ShrBaseObjectFactory.js │ │ │ └── ShrId.js │ │ ├── core │ │ │ ├── Abatement.js │ │ │ ├── AccessTime.js │ │ │ ├── AccessionIdentifier.js │ │ │ ├── ActionPerformed.js │ │ │ ├── ActionRequested.js │ │ │ ├── ActionStatement.js │ │ │ ├── ActionTaken.js │ │ │ ├── Actual.js │ │ │ ├── AdditionalDosageInstruction.js │ │ │ ├── Additive.js │ │ │ ├── Address.js │ │ │ ├── AddressLine.js │ │ │ ├── AdministrativeGender.js │ │ │ ├── AdverseDrugReaction.js │ │ │ ├── AdverseEvent.js │ │ │ ├── AdverseEventCondition.js │ │ │ ├── AdverseEventRecorder.js │ │ │ ├── AdverseEventSubjectOfRecord.js │ │ │ ├── Affiliation.js │ │ │ ├── Age.js │ │ │ ├── AgeGroup.js │ │ │ ├── Alias.js │ │ │ ├── AllergyIntolerance.js │ │ │ ├── AllergyIntoleranceReaction.js │ │ │ ├── AllergyRecorder.js │ │ │ ├── AlleviatingFactor.js │ │ │ ├── Altitude.js │ │ │ ├── AmountOrSize.js │ │ │ ├── AnatomicalDirection.js │ │ │ ├── Annotation.js │ │ │ ├── AnnotationAuthor.js │ │ │ ├── AnonymizedFlag.js │ │ │ ├── Answer.js │ │ │ ├── AnswerOption.js │ │ │ ├── AnswerValue.js │ │ │ ├── AnswerValueSet.js │ │ │ ├── ApplicableAgeRange.js │ │ │ ├── ApplicableSubpopulation.js │ │ │ ├── Appointment.js │ │ │ ├── AppointmentParticipant.js │ │ │ ├── AppointmentParticipation.js │ │ │ ├── ApprovalDate.js │ │ │ ├── AsNeededIndicator.js │ │ │ ├── AssociatedStudy.js │ │ │ ├── Attachment.js │ │ │ ├── Authenticator.js │ │ │ ├── BeginDateTime.js │ │ │ ├── BinaryData.js │ │ │ ├── BirthSex.js │ │ │ ├── BloodPressure.js │ │ │ ├── BloodPressureCuffSize.js │ │ │ ├── BodyHeight.js │ │ │ ├── BodyLength.js │ │ │ ├── BodyLocation.js │ │ │ ├── BodyMassIndex.js │ │ │ ├── BodyPosition.js │ │ │ ├── BodyStructure.js │ │ │ ├── BodyTemperature.js │ │ │ ├── BodyWeight.js │ │ │ ├── Brand.js │ │ │ ├── BrandName.js │ │ │ ├── Capacity.js │ │ │ ├── CareContext.js │ │ │ ├── CareManager.js │ │ │ ├── Category.js │ │ │ ├── CausalAttribution.js │ │ │ ├── CauseCategory.js │ │ │ ├── Certainty.js │ │ │ ├── City.js │ │ │ ├── ClassHistory.js │ │ │ ├── ClinicalContext.js │ │ │ ├── ClinicalNote.js │ │ │ ├── ClinicalStatement.js │ │ │ ├── ClinicalStatus.js │ │ │ ├── ClockFaceDirection.js │ │ │ ├── Code.js │ │ │ ├── CodeSet.js │ │ │ ├── CodeSetConcept.js │ │ │ ├── CodeSetFilter.js │ │ │ ├── CodeSystem.js │ │ │ ├── CodeSystemVersion.js │ │ │ ├── CodeValue.js │ │ │ ├── CodeableConcept.js │ │ │ ├── CodedLaboratoryObservation.js │ │ │ ├── CodedNonLaboratoryObservation.js │ │ │ ├── CodedObservationComponent.js │ │ │ ├── CodedSocialHistoryObservation.js │ │ │ ├── Coding.js │ │ │ ├── CollectionMethod.js │ │ │ ├── CollectionSite.js │ │ │ ├── CollectionSource.js │ │ │ ├── CollectionTime.js │ │ │ ├── CommentOrDescription.js │ │ │ ├── Communication.js │ │ │ ├── CommunicationMethod.js │ │ │ ├── ComorbidCondition.js │ │ │ ├── Comparator.js │ │ │ ├── ComponentOnlyNonLaboratoryObservation.js │ │ │ ├── Components.js │ │ │ ├── Composition.js │ │ │ ├── Conclusion.js │ │ │ ├── Condition.js │ │ │ ├── ConditionCause.js │ │ │ ├── CongenitalAbnormality.js │ │ │ ├── ContactDetail.js │ │ │ ├── ContactPoint.js │ │ │ ├── ContemporaneousPatientInformation.js │ │ │ ├── ContentLogicalDefinition.js │ │ │ ├── ContentType.js │ │ │ ├── ContextValue.js │ │ │ ├── Copyright.js │ │ │ ├── CorrectionFactor.js │ │ │ ├── Count.js │ │ │ ├── CountPerInterval.js │ │ │ ├── Country.js │ │ │ ├── CountryOfIssue.js │ │ │ ├── CreationDateTime.js │ │ │ ├── Criticality.js │ │ │ ├── DailyLifeEvent.js │ │ │ ├── DataAbsentReason.js │ │ │ ├── DataAsString.js │ │ │ ├── DataValue.js │ │ │ ├── DateOfBirth.js │ │ │ ├── DateOfDiagnosis.js │ │ │ ├── DayOfWeek.js │ │ │ ├── Deceased.js │ │ │ ├── Denominator.js │ │ │ ├── DescriptionMarkdown.js │ │ │ ├── Designation.js │ │ │ ├── DetailsLink.js │ │ │ ├── Device.js │ │ │ ├── DeviceUdi.js │ │ │ ├── DiagnosisCode.js │ │ │ ├── DiagnosticImaging.js │ │ │ ├── DiagnosticReport.js │ │ │ ├── DiagnosticReportParticipant.js │ │ │ ├── DiastolicPressure.js │ │ │ ├── Dimensions.js │ │ │ ├── DisplayText.js │ │ │ ├── Distance.js │ │ │ ├── District.js │ │ │ ├── DocumentAuthor.js │ │ │ ├── DocumentReference.js │ │ │ ├── DocumentReferenced.js │ │ │ ├── DocumentStatus.js │ │ │ ├── DomainResource.js │ │ │ ├── Dosage.js │ │ │ ├── DosageBodyLocation.js │ │ │ ├── DosageInstructionsText.js │ │ │ ├── DosageMethod.js │ │ │ ├── DoseAmount.js │ │ │ ├── DoseForm.js │ │ │ ├── DriversLicenseInformation.js │ │ │ ├── DriversLicenseNumber.js │ │ │ ├── Duration.js │ │ │ ├── DurationRange.js │ │ │ ├── ECOGPerformanceStatus.js │ │ │ ├── EffectiveTimePeriod.js │ │ │ ├── EmbeddedContent.js │ │ │ ├── EnableWhen.js │ │ │ ├── Encounter.js │ │ │ ├── EncounterClass.js │ │ │ ├── EncounterDiagnosis.js │ │ │ ├── EncounterLocation.js │ │ │ ├── EncounterOrEpisode.js │ │ │ ├── EncounterParticipant.js │ │ │ ├── EndDateTime.js │ │ │ ├── Enrollment.js │ │ │ ├── Entity.js │ │ │ ├── EntityOrRole.js │ │ │ ├── EpisodeOfCare.js │ │ │ ├── Ethnicity.js │ │ │ ├── EthnicityCode.js │ │ │ ├── EthnicityDetail.js │ │ │ ├── Event.js │ │ │ ├── EventDuration.js │ │ │ ├── Evidence.js │ │ │ ├── ExacerbatingFactor.js │ │ │ ├── ExcludeCodes.js │ │ │ ├── ExcludeFlag.js │ │ │ ├── ExpansionCoding.js │ │ │ ├── ExpansionParameter.js │ │ │ ├── ExpectedPerformanceTime.js │ │ │ ├── ExpectedPerformer.js │ │ │ ├── ExpectedPerformerType.js │ │ │ ├── ExpirationDate.js │ │ │ ├── FacilityType.js │ │ │ ├── FamilyName.js │ │ │ ├── FathersName.js │ │ │ ├── FictionalPersonFlag.js │ │ │ ├── FillerOrderNumber.js │ │ │ ├── FocalDevice.js │ │ │ ├── FocusOfResponse.js │ │ │ ├── Formalism.js │ │ │ ├── Frames.js │ │ │ ├── Frequency.js │ │ │ ├── Geoposition.js │ │ │ ├── GestationalAge.js │ │ │ ├── GestationalTimePeriod.js │ │ │ ├── GivenName.js │ │ │ ├── Group.js │ │ │ ├── GroupCharacteristic.js │ │ │ ├── GroupCharacteristicValue.js │ │ │ ├── HandlingRisk.js │ │ │ ├── HasAnswer.js │ │ │ ├── Hash.js │ │ │ ├── HeadCircumference.js │ │ │ ├── HeadlessLaboratoryPanel.js │ │ │ ├── HeadlessPanel.js │ │ │ ├── HeartRate.js │ │ │ ├── HumanName.js │ │ │ ├── Identifier.js │ │ │ ├── IdentifierString.js │ │ │ ├── ImagingProcedure.js │ │ │ ├── ImagingSubstance.js │ │ │ ├── ImplicitRules.js │ │ │ ├── Inactive.js │ │ │ ├── InactiveFlag.js │ │ │ ├── IncludeCodes.js │ │ │ ├── IncludeInactiveCodes.js │ │ │ ├── Indication.js │ │ │ ├── Ingredient.js │ │ │ ├── IngredientAmount.js │ │ │ ├── IntegerQuantity.js │ │ │ ├── Interpretation.js │ │ │ ├── IsActiveIngredient.js │ │ │ ├── IsBrand.js │ │ │ ├── IsExperimental.js │ │ │ ├── IsExtensible.js │ │ │ ├── IsImmutable.js │ │ │ ├── IsReadOnly.js │ │ │ ├── IsRequired.js │ │ │ ├── Issuer.js │ │ │ ├── Jurisdiction.js │ │ │ ├── KarnofskyPerformanceStatus.js │ │ │ ├── LaboratoryObservation.js │ │ │ ├── LaboratoryPanel.js │ │ │ ├── LaboratoryProcedure.js │ │ │ ├── LandmarkLocation.js │ │ │ ├── LandmarkToBodyLocationDirection.js │ │ │ ├── LandmarkToBodyLocationDistance.js │ │ │ ├── LandmarkType.js │ │ │ ├── Language.js │ │ │ ├── LanguageQualifier.js │ │ │ ├── LastReviewedDate.js │ │ │ ├── LastUpdated.js │ │ │ ├── Laterality.js │ │ │ ├── Latitude.js │ │ │ ├── LifeEventOffset.js │ │ │ ├── Location.js │ │ │ ├── LocationCode.js │ │ │ ├── LocationName.js │ │ │ ├── LockedDate.js │ │ │ ├── Longitude.js │ │ │ ├── LotNumber.js │ │ │ ├── LowerBound.js │ │ │ ├── LowerLimit.js │ │ │ ├── ManagingOrganization.js │ │ │ ├── Manifestation.js │ │ │ ├── ManufactureDate.js │ │ │ ├── Manufacturer.js │ │ │ ├── ManufacturerName.js │ │ │ ├── MaritalStatus.js │ │ │ ├── MasterIdentifier.js │ │ │ ├── MaterialUsed.js │ │ │ ├── MaxCount.js │ │ │ ├── MaxTextLength.js │ │ │ ├── MaximumDosePerTimePeriod.js │ │ │ ├── MayRepeat.js │ │ │ ├── Media.js │ │ │ ├── MediaSubjectOfRecord.js │ │ │ ├── MedicalInterpreter.js │ │ │ ├── MedicalInterpreterNeeded.js │ │ │ ├── Medication.js │ │ │ ├── MedicationAdministration.js │ │ │ ├── MedicationCodeOrReference.js │ │ │ ├── MedicationDispense.js │ │ │ ├── MedicationExposure.js │ │ │ ├── MedicationNonAdherenceReason.js │ │ │ ├── MedicationNonadherence.js │ │ │ ├── MedicationRequest.js │ │ │ ├── MedicationRequester.js │ │ │ ├── MedicationStatement.js │ │ │ ├── MedicationStatementInformationSource.js │ │ │ ├── MedicationStatementRelatedRequest.js │ │ │ ├── Member.js │ │ │ ├── MemberParticipation.js │ │ │ ├── Metadata.js │ │ │ ├── Method.js │ │ │ ├── MiddleName.js │ │ │ ├── MillisecondsBetweenSamples.js │ │ │ ├── MinCount.js │ │ │ ├── MobileFacility.js │ │ │ ├── Mode.js │ │ │ ├── Money.js │ │ │ ├── MostRecentOccurrenceTime.js │ │ │ ├── MothersMaidenName.js │ │ │ ├── MultipleBirth.js │ │ │ ├── Name.js │ │ │ ├── NameAsText.js │ │ │ ├── Narrative.js │ │ │ ├── NarrativeQualifier.js │ │ │ ├── NarrativeText.js │ │ │ ├── NationalProviderIdentifier.js │ │ │ ├── Need.js │ │ │ ├── NonLaboratoryObservation.js │ │ │ ├── NonOccurrenceTimeOrPeriod.js │ │ │ ├── NonSelectable.js │ │ │ ├── Number.js │ │ │ ├── NumberOfRefillsAllowed.js │ │ │ ├── NumberOfRepeats.js │ │ │ ├── NumberOfSecondsDuration.js │ │ │ ├── Numerator.js │ │ │ ├── Observation.js │ │ │ ├── ObservationComponent.js │ │ │ ├── ObservationOrProcedure.js │ │ │ ├── ObservationSubjectOfRecord.js │ │ │ ├── OccurrenceDuration.js │ │ │ ├── OccurrencePeriod.js │ │ │ ├── OccurrenceTime.js │ │ │ ├── OccurrenceTimeOrPeriod.js │ │ │ ├── Offset.js │ │ │ ├── OnBehalfOf.js │ │ │ ├── Onset.js │ │ │ ├── Operation.js │ │ │ ├── OperationalStatus.js │ │ │ ├── Organization.js │ │ │ ├── OrganizationName.js │ │ │ ├── Orientation.js │ │ │ ├── Origin.js │ │ │ ├── OriginalReport.js │ │ │ ├── Outcome.js │ │ │ ├── OverTheCounter.js │ │ │ ├── OxygenSaturation.js │ │ │ ├── Package.js │ │ │ ├── Panel.js │ │ │ ├── PanelMembers.js │ │ │ ├── ParameterValue.js │ │ │ ├── ParsableContent.js │ │ │ ├── PartOf.js │ │ │ ├── Participant.js │ │ │ ├── Participation.js │ │ │ ├── ParticipationPeriod.js │ │ │ ├── ParticipationType.js │ │ │ ├── PassportInformation.js │ │ │ ├── PassportNumber.js │ │ │ ├── Patient.js │ │ │ ├── PatientSubjectOfRecord.js │ │ │ ├── Percentage.js │ │ │ ├── Performer.js │ │ │ ├── Person.js │ │ │ ├── PersonInformationSource.js │ │ │ ├── PersonParticipant.js │ │ │ ├── PhotographicImage.js │ │ │ ├── PhysicalType.js │ │ │ ├── PixelHeight.js │ │ │ ├── PixelWidth.js │ │ │ ├── PlaceOfBirth.js │ │ │ ├── PlacerOrderNumber.js │ │ │ ├── PossibleCause.js │ │ │ ├── PossibleDrugCause.js │ │ │ ├── PostalCode.js │ │ │ ├── Practitioner.js │ │ │ ├── PractitionerOrOrganizationPerformer.js │ │ │ ├── PractitionerPerformer.js │ │ │ ├── PractitionerProcedureParticipant.js │ │ │ ├── Precondition.js │ │ │ ├── Preferred.js │ │ │ ├── Prefix.js │ │ │ ├── PrepopulateValue.js │ │ │ ├── PrincipalInvestigator.js │ │ │ ├── PriorityCode.js │ │ │ ├── PriorityRank.js │ │ │ ├── PriorityRankUnsignedInt.js │ │ │ ├── Procedure.js │ │ │ ├── ProcedureParticipant.js │ │ │ ├── ProcedureRequest.js │ │ │ ├── ProcedureRequester.js │ │ │ ├── Profile.js │ │ │ ├── Property.js │ │ │ ├── PublisherName.js │ │ │ ├── Purpose.js │ │ │ ├── PurposeMarkdown.js │ │ │ ├── Qualification.js │ │ │ ├── QuantitativeLaboratoryObservation.js │ │ │ ├── QuantitativeNonLaboratoryObservation.js │ │ │ ├── QuantitativeObservationComponent.js │ │ │ ├── Quantity.js │ │ │ ├── QuantityPerDispense.js │ │ │ ├── Question.js │ │ │ ├── Questionnaire.js │ │ │ ├── QuestionnaireItem.js │ │ │ ├── QuestionnaireResponse.js │ │ │ ├── QuestionnaireResponseItem.js │ │ │ ├── QuestionnaireResponseRecorder.js │ │ │ ├── Race.js │ │ │ ├── RaceCode.js │ │ │ ├── RaceDetail.js │ │ │ ├── RadiationDosePerFraction.js │ │ │ ├── RadiationFractionsDelivered.js │ │ │ ├── RadiationProcedure.js │ │ │ ├── Range.js │ │ │ ├── Ratio.js │ │ │ ├── ReasonCode.js │ │ │ ├── ReasonReference.js │ │ │ ├── ReceivedTime.js │ │ │ ├── Recipient.js │ │ │ ├── RecurrenceInterval.js │ │ │ ├── RecurrencePattern.js │ │ │ ├── RecurrenceRange.js │ │ │ ├── ReferenceRange.js │ │ │ ├── ReferralOrProcedureRequest.js │ │ │ ├── ReferralRecipient.js │ │ │ ├── ReferralRequest.js │ │ │ ├── ReferralRequester.js │ │ │ ├── ReferralSubjectOfRecord.js │ │ │ ├── RelatedDocument.js │ │ │ ├── RelatedInformation.js │ │ │ ├── RelatedPerson.js │ │ │ ├── RelatedRequest.js │ │ │ ├── RelationToLandmark.js │ │ │ ├── Relationship.js │ │ │ ├── RelationshipToPatient.js │ │ │ ├── RelevantTime.js │ │ │ ├── Replaces.js │ │ │ ├── RequestIntent.js │ │ │ ├── ResearchStudy.js │ │ │ ├── ResearchSubject.js │ │ │ ├── Resource.js │ │ │ ├── ResourceLocation.js │ │ │ ├── ResourceSize.js │ │ │ ├── RespiratoryRate.js │ │ │ ├── Role.js │ │ │ ├── RouteIntoBody.js │ │ │ ├── SampledData.js │ │ │ ├── Section.js │ │ │ ├── SecurityLabel.js │ │ │ ├── SequenceNumber.js │ │ │ ├── Seriousness.js │ │ │ ├── ServiceCategory.js │ │ │ ├── ServiceType.js │ │ │ ├── Setting.js │ │ │ ├── Severity.js │ │ │ ├── ShrCoreObjectFactory.js │ │ │ ├── Signatory.js │ │ │ ├── Signature.js │ │ │ ├── SignatureType.js │ │ │ ├── SimpleCodedLaboratoryObservation.js │ │ │ ├── SimpleCodedNonLaboratoryObservation.js │ │ │ ├── SimpleLaboratoryObservation.js │ │ │ ├── SimpleNonLaboratoryObservation.js │ │ │ ├── SimpleQuantitativeLaboratoryObservation.js │ │ │ ├── SimpleQuantity.js │ │ │ ├── SituationStatement.js │ │ │ ├── SocialHistoryObservation.js │ │ │ ├── SocialSecurityNumber.js │ │ │ ├── SpecialHandling.js │ │ │ ├── Specialty.js │ │ │ ├── Specimen.js │ │ │ ├── SpecimenContainer.js │ │ │ ├── SpecimenQuantity.js │ │ │ ├── SpecimenTreatment.js │ │ │ ├── SpecimenType.js │ │ │ ├── SpokenLanguageProficiency.js │ │ │ ├── Sponsor.js │ │ │ ├── StageDetail.js │ │ │ ├── StageInformation.js │ │ │ ├── StageSummary.js │ │ │ ├── State.js │ │ │ ├── StateOfIssue.js │ │ │ ├── StatementDateTime.js │ │ │ ├── Statistic.js │ │ │ ├── StatisticType.js │ │ │ ├── Status.js │ │ │ ├── StatusHistory.js │ │ │ ├── StudyArm.js │ │ │ ├── SubjectOfInformationCode.js │ │ │ ├── SubjectOfRecord.js │ │ │ ├── SubjectType.js │ │ │ ├── Substance.js │ │ │ ├── SubstanceCategory.js │ │ │ ├── SubstanceCode.js │ │ │ ├── SubstanceOrCode.js │ │ │ ├── SubstitutionAllowed.js │ │ │ ├── SubstitutionReason.js │ │ │ ├── Subtype.js │ │ │ ├── Suffix.js │ │ │ ├── SupplementalOxygenConcentration.js │ │ │ ├── SupplementalOxygenFlowrate.js │ │ │ ├── SupplyDuration.js │ │ │ ├── SurgicalBodyLocation.js │ │ │ ├── SurgicalBodyLocationRole.js │ │ │ ├── SurgicalProcedure.js │ │ │ ├── SystolicPressure.js │ │ │ ├── Tag.js │ │ │ ├── TaxIdentificationNumber.js │ │ │ ├── TelecomNumberOrAddress.js │ │ │ ├── TerminationReason.js │ │ │ ├── Text.js │ │ │ ├── TimeOfDay.js │ │ │ ├── TimePeriod.js │ │ │ ├── Timing.js │ │ │ ├── TimingCode.js │ │ │ ├── TimingOfDoses.js │ │ │ ├── Title.js │ │ │ ├── TotalCount.js │ │ │ ├── TotalRadiationDoseDelivered.js │ │ │ ├── TreatmentIntent.js │ │ │ ├── Type.js │ │ │ ├── UnitedStatesAddress.js │ │ │ ├── UnitedStatesState.js │ │ │ ├── Units.js │ │ │ ├── UpperBound.js │ │ │ ├── UpperLimit.js │ │ │ ├── Url.js │ │ │ ├── UseContext.js │ │ │ ├── ValueSet.js │ │ │ ├── ValueSetExpansion.js │ │ │ ├── ValueSetUri.js │ │ │ ├── VendorModelNumber.js │ │ │ ├── VersionId.js │ │ │ ├── VersionString.js │ │ │ ├── View.js │ │ │ ├── VitalSign.js │ │ │ ├── VitalSignsPanel.js │ │ │ ├── WhenClinicallyRecognized.js │ │ │ └── WrittenLanguageProficiency.js │ │ ├── fhx │ │ │ ├── ConditionOutcome.js │ │ │ ├── FamilyMember.js │ │ │ ├── FamilyMemberCondition.js │ │ │ ├── FamilyMemberHistory.js │ │ │ └── ShrFhxObjectFactory.js │ │ ├── financial │ │ │ ├── Beneficiary.js │ │ │ ├── Coverage.js │ │ │ ├── CoverageType.js │ │ │ ├── PolicyHolder.js │ │ │ ├── ShrFinancialObjectFactory.js │ │ │ ├── Subscriber.js │ │ │ └── SubscriberId.js │ │ ├── lab │ │ │ ├── AlanineAminotransferaseCCncPtSerPlasQnLabObs.js │ │ │ ├── AlbuminGlobulinMRtoPtSerPlasQnLabObs.js │ │ │ ├── AlbuminMCncPtSerPlasQnLabObs.js │ │ │ ├── AlkalinePhosphataseCCncPtSerPlasQnLabObs.js │ │ │ ├── AniongapSCncPtSerPlasQnLabObs.js │ │ │ ├── AspartateAminotransferaseCCncPtSerPlasQnLabObs.js │ │ │ ├── Basophils100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── BasophilsNCncPtBldQnAutoCntLabObs.js │ │ │ ├── BicarbonateSCncPtSerPlasQnLabObs.js │ │ │ ├── BilirubinDirectMCncPtSerPlasQnLabObs.js │ │ │ ├── BilirubinMCncPtSerPlasQnLabObs.js │ │ │ ├── Blasts100WBCNFrPtBldQnManCntLabObs.js │ │ │ ├── BlastsNCncPtBldQnLabObs.js │ │ │ ├── CBCWAutoDifferentialPanel.js │ │ │ ├── CalciumMCncPtSerPlasQnLabObs.js │ │ │ ├── CarbonDioxideSCncPtSerPlasQnLabObs.js │ │ │ ├── ChlorideSCncPtSerPlasQnLabObs.js │ │ │ ├── ComprehensiveMetabolic2000SerumOrPlasmaPanel.js │ │ │ ├── CreatinineMCncPtSerPlasQnLabObs.js │ │ │ ├── Eosinophils100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── EosinophilsNCncPtBldQnAutoCntLabObs.js │ │ │ ├── ErythrocyteDistributionWidthEntVolPtRBCQnLabObs.js │ │ │ ├── ErythrocyteDistributionWidthRatioPtRBCQnAutoCntLabObs.js │ │ │ ├── ErythrocyteMCHCMCncPtRBCQnAutoCntLabObs.js │ │ │ ├── ErythrocyteMCHEntMassPtRBCQnAutoCntLabObs.js │ │ │ ├── ErythrocytesNCncPtBldQnAutoCntLabObs.js │ │ │ ├── ErythrocytesNucleated100WBCRatioPtBldQnAutoCntLabObs.js │ │ │ ├── ErythrocytesNucleated100WBCRatioPtBldQnLabObs.js │ │ │ ├── ErythrocytesNucleatedNCncPtBldQnLabObs.js │ │ │ ├── GFR173sqMPredArVRatPtSerPlasQnMDRDLabObs.js │ │ │ ├── GFR173sqMPredBlackArVRatPtSerPlasBldQnMDRDLabObs.js │ │ │ ├── GFR173sqMPredFemaleArVRatPtSerPlasBldQnMDRDLabObs.js │ │ │ ├── GFR173sqMPredNonBlackArVRatPtSerPlasBldQnMDRDLabObs.js │ │ │ ├── GlobulinMCncPtSerQnCalculatedLabObs.js │ │ │ ├── GlucoseMCncPtSerPlasQnLabObs.js │ │ │ ├── Granulocytes100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── GranulocytesImmature100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── GranulocytesImmatureNCncPtBldQnAutoCntLabObs.js │ │ │ ├── HematocritVFrPtBldQnAutoCntLabObs.js │ │ │ ├── HemoglobinMCncPtBldQnLabObs.js │ │ │ ├── LeukocytesNCncPtBldQnAutoCntLabObs.js │ │ │ ├── LeukocytesOtherNCncPtBldQnAutoCntLabObs.js │ │ │ ├── Lymphocytes100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── LymphocytesNCncPtBldQnAutoCntLabObs.js │ │ │ ├── LymphocytesVariant100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── LymphocytesVariant100WBCNFrPtBldQnLabObs.js │ │ │ ├── LymphocytesVariantNCncPtBldQnAutoCntLabObs.js │ │ │ ├── MeanCorpuscularVolumeEntVolPtRBCQnAutoCntLabObs.js │ │ │ ├── Metamyelocytes100WBCNFrPtBldQnManCntLabObs.js │ │ │ ├── MetamyelocytesNCncPtBldQnLabObs.js │ │ │ ├── Monocytes100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── MonocytesNCncPtBldQnAutoCntLabObs.js │ │ │ ├── MorphologyImpPtBldNarLabObs.js │ │ │ ├── Myelocytes100WBCNFrPtBldQnManCntLabObs.js │ │ │ ├── MyelocytesNCncPtBldQnLabObs.js │ │ │ ├── Neutrophils100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── NeutrophilsBandForm100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── NeutrophilsBandForm100WBCNFrPtBldQnManCntLabObs.js │ │ │ ├── NeutrophilsBandFormNCncPtBldQnAutoCntLabObs.js │ │ │ ├── NeutrophilsBandFormNCncPtBldQnLabObs.js │ │ │ ├── NeutrophilsNCncPtBldQnAutoCntLabObs.js │ │ │ ├── OtherCells100WBCNFrPtBldQnAutoCntLabObs.js │ │ │ ├── OtherCellsNCncPtBldQnAutoCntLabObs.js │ │ │ ├── PlateletDistributionWidthEntVolPtBldQnAutoCntLabObs.js │ │ │ ├── PlateletMeanVolumeEntVolPtBldQnAutoCntLabObs.js │ │ │ ├── PlateletMeanVolumeEntVolPtBldQnReesEckerLabObs.js │ │ │ ├── PlateletsNCncPtBldQnAutoCntLabObs.js │ │ │ ├── PotassiumSCncPtSerPlasQnLabObs.js │ │ │ ├── Promyelocytes100WBCNFrPtBldQnManCntLabObs.js │ │ │ ├── PromyelocytesNCncPtBldQnLabObs.js │ │ │ ├── ProteinMCncPtSerPlasQnLabObs.js │ │ │ ├── QuantityWithRequiredUnits.js │ │ │ ├── ShrLabObjectFactory.js │ │ │ ├── SimpleQuantLabWithRequiredUnits.js │ │ │ ├── SodiumSCncPtSerPlasQnLabObs.js │ │ │ ├── UreaNitrogenCreatinineMRtoPtSerPlasQnLabObs.js │ │ │ └── UreaNitrogenMCncPtSerPlasQnLabObs.js │ │ └── sdoh │ │ │ ├── AlcoholBingeEpisodes.js │ │ │ ├── AlcoholUse.js │ │ │ ├── AnimalExposure.js │ │ │ ├── Coinhabitant.js │ │ │ ├── DomesticViolence.js │ │ │ ├── EducationalAttainment.js │ │ │ ├── EmotionalSafety.js │ │ │ ├── ExposureAmount.js │ │ │ ├── ExposureMode.js │ │ │ ├── ExposureReason.js │ │ │ ├── ExposureRoute.js │ │ │ ├── FinancialStability.js │ │ │ ├── ForeignCountryOfTravelTwoYears.js │ │ │ ├── HomeEnvironmentRisk.js │ │ │ ├── HouseholdIncome.js │ │ │ ├── HouseholdSize.js │ │ │ ├── HousingSecurity.js │ │ │ ├── IncomeAdequacy.js │ │ │ ├── IncomeSource.js │ │ │ ├── IntravenousDrugUse.js │ │ │ ├── NicotineExposure.js │ │ │ ├── NonCashBenefit.js │ │ │ ├── NumberOfDependents.js │ │ │ ├── PhysicalSafety.js │ │ │ ├── PrenatalExposure.js │ │ │ ├── ShrSdohObjectFactory.js │ │ │ ├── SubstanceOrAgent.js │ │ │ ├── SubstanceUse.js │ │ │ ├── TobaccoSmokingStatusNHIS.js │ │ │ ├── TransportationAvailability.js │ │ │ ├── TroubleAffordingChildCare.js │ │ │ ├── TroubleAffordingDentalCare.js │ │ │ ├── TroubleAffordingFood.js │ │ │ ├── TroubleAffordingHousing.js │ │ │ ├── TroubleAffordingMedication.js │ │ │ ├── TroubleAffordingTransportation.js │ │ │ ├── TroubleAffordingUtilities.js │ │ │ └── UnprescribedExposure.js │ └── valueSets.js ├── nav │ ├── NavBar.css │ └── NavBar.jsx ├── noteparser │ ├── NoteParser.js │ ├── app.js │ └── samples │ │ ├── note1.txt │ │ ├── note10.txt │ │ ├── note11.txt │ │ ├── note12.txt │ │ ├── note13.txt │ │ ├── note14.txt │ │ ├── note2.txt │ │ ├── note3.txt │ │ ├── note4.txt │ │ ├── note5.txt │ │ ├── note6.txt │ │ ├── note7.txt │ │ ├── note8.txt │ │ └── note9.txt ├── notes │ ├── ActiveContextsBreadcrumbs.jsx │ ├── ActiveContextsBreadcrumbs.scss │ ├── CompletionComponentFactory.js │ ├── CompletionPortalPlugin.jsx │ ├── EditorToolbar.css │ ├── EditorToolbar.jsx │ ├── FillPlaceholder.jsx │ ├── FillPlaceholder.scss │ ├── FluxNotesEditor.jsx │ ├── FluxNotesEditor.scss │ ├── InMemoryClinicalNote.jsx │ ├── KeyboardShortcutsPlugin.js │ ├── KeywordStructuredFieldPlugin.jsx │ ├── NLPHashtagPlugin.jsx │ ├── NoteAssistant.jsx │ ├── NoteAssistant.scss │ ├── PointOfCare.css │ ├── PointOfCare.jsx │ ├── StructuredFieldPlugin.jsx │ ├── SuggestionPortalPlaceholderSearchIndex.jsx │ ├── SuggestionPortalSearchIndex.jsx │ ├── SuggestionPortalShortcutSearchIndex.jsx │ └── fillFieldComponents │ │ ├── ButtonSetFillFieldForPlaceholder.jsx │ │ ├── MenuItemSetFillForPlaceholder.jsx │ │ ├── MenuItemSetFillForPlaceholder.scss │ │ ├── MultiButtonSetFillFieldForPlaceholder.jsx │ │ ├── SearchableListForPlaceholder.css │ │ └── SearchableListForPlaceholder.jsx ├── panels │ ├── NotesPanel.jsx │ ├── NotesPanel.scss │ ├── PatientControlPanel.jsx │ ├── PatientControlPanel.scss │ ├── PickListOptionsPanel.jsx │ ├── TargetedDataPanel.jsx │ └── TargetedDataPanel.scss ├── patient-date-updater │ └── app.js ├── patient │ └── PatientRecord.jsx ├── patientControl │ ├── BaseIndexer.js │ ├── ClusterPointsIndexer.js │ ├── ColumnsIndexer.js │ ├── DiseaseStatusValuesIndexer.js │ ├── EventsIndexer.js │ ├── MedicationsIndexer.js │ ├── NameValuePairsIndexer.js │ ├── NoteContentIndexer.js │ ├── NotesIndexer.js │ ├── PatientSearch.jsx │ ├── PatientSearch.scss │ ├── PatientSelectionModal.jsx │ ├── PatientSelectionModal.scss │ ├── ReviewOfSystemsValuesIndexer.js │ ├── SearchIndex.js │ ├── SearchSuggestion.jsx │ ├── SearchSuggestion.scss │ └── ValueOverTimeIndexer.js ├── preferences │ ├── IPreferenceStore.jsx │ ├── LocalStoragePreferenceStore.jsx │ └── PreferenceManager.jsx ├── reducers │ ├── index.js │ └── mcode.js ├── security │ ├── SecurityManager.jsx │ └── UserProfile.jsx ├── shortcuts │ ├── CreatorBase.jsx │ ├── CreatorChild.jsx │ ├── CreatorIntermediary.jsx │ ├── EntryShortcut.jsx │ ├── InsertValue.jsx │ ├── NLPHashtag.jsx │ ├── Placeholder.jsx │ ├── Shortcut.jsx │ ├── ShortcutManager.js │ ├── ShortcutUtils.js │ ├── Shortcuts.json │ ├── StructuredFieldMapManager.js │ └── UpdaterBase.jsx ├── store │ └── configureStore.js ├── styles │ ├── CompassApp.css │ ├── FullApp.scss │ ├── LandingPage.scss │ ├── Pilot2MvpApp.scss │ ├── PointOfCareApp.scss │ ├── SlimApp.css │ ├── _variables.scss │ └── mixins │ │ ├── _colors.scss │ │ └── _media-queries.scss ├── summary │ ├── .gitignore │ ├── BandedLineChartVisualizer.jsx │ ├── BandedLineChartVisualizer.scss │ ├── ClinicalEventSelection.css │ ├── ClinicalEventSelection.jsx │ ├── CompassAppSummaryMetadata.jsx │ ├── ConditionSelection.jsx │ ├── ConditionSelection.scss │ ├── ExpandedTableVisualizer.jsx │ ├── ExpandedTableVisualizer.scss │ ├── FormatMedicationChange.js │ ├── FormatTabularListVisualizer.css │ ├── LongitudinalTable.jsx │ ├── LongitudinalTable.scss │ ├── LongitudinalTableVisualizer.jsx │ ├── MedicationRangeChartVisualizer.jsx │ ├── MedicationRangeChartVisualizer.scss │ ├── NarrativeNameValuePairsVisualizer.jsx │ ├── NarrativeNameValuePairsVisualizer.scss │ ├── Pilot2MvpAppSummaryMetadata.jsx │ ├── ProgressionLineChartVisualizer.jsx │ ├── ProgressionLineChartVisualizer.scss │ ├── RangeChart.css │ ├── RangeChart.jsx │ ├── ScatterPlotVisualizer.css │ ├── ScatterPlotVisualizer.jsx │ ├── SummaryHeader.jsx │ ├── SummaryHeader.scss │ ├── SummaryMetadata.jsx │ ├── TabularListVisualizer.jsx │ ├── TabularListVisualizer.scss │ ├── TabularListVisualizerTable.jsx │ ├── TargetedDataSection.jsx │ ├── TargetedDataSection.scss │ ├── TargetedDataSubpanel.css │ ├── TargetedDataSubpanel.jsx │ ├── TreatmentData.js │ ├── Visualizer.jsx │ ├── VisualizerManager.jsx │ ├── VisualizerMenu.jsx │ ├── activeTreatmentSummary │ │ ├── ActiveTreatmentSummaryObjectFactory.js │ │ ├── CancerDisorderPresentActiveTreatmentSummary.js │ │ └── IActiveTreatmentSummary.js │ ├── matchers │ │ ├── AlwaysMatcher.js │ │ ├── FunctionMatcher.js │ │ ├── Matcher.js │ │ └── StringMatcher.js │ └── metadata │ │ ├── ActiveConditionsSection.jsx │ │ ├── ActiveConditionsSubsection.jsx │ │ ├── ActiveTreatmentsSubsection.jsx │ │ ├── AllergiesSection.jsx │ │ ├── BloodPressureSubsection.jsx │ │ ├── BreastCancerMetadata.jsx │ │ ├── ClinicalTrialsSection.jsx │ │ ├── DefaultCompassAppMetadata.jsx │ │ ├── DefaultMetadata.jsx │ │ ├── DefaultPilot2MvpAppMetadata.jsx │ │ ├── DetailedTreatmentOptionsSection.jsx │ │ ├── DiseaseStatusSection.jsx │ │ ├── GeneralCancerSummarySection.jsx │ │ ├── HeartRateSubsection.jsx │ │ ├── HemoglobinSubsection.jsx │ │ ├── ImagingSection.jsx │ │ ├── KeyDatesSubsection.jsx │ │ ├── LabTestSubsection.jsx │ │ ├── McodeMetadata.jsx │ │ ├── MedicationsColumnsSection.jsx │ │ ├── MedicationsSection.jsx │ │ ├── MetadataSection.jsx │ │ ├── MostRecentVisitsSubsection.jsx │ │ ├── NeutrophilCountSubsection.jsx │ │ ├── PathologySection.jsx │ │ ├── Pilot2MvpMetadata.jsx │ │ ├── PlateletSubsection.jsx │ │ ├── ProceduresSection.jsx │ │ ├── RecentLabResultsSubsection.jsx │ │ ├── RecentToxicitiesSubsection.jsx │ │ ├── ReviewOfSystemsSection.jsx │ │ ├── SarcomaCompassAppMetadata.jsx │ │ ├── SarcomaConditionSummarySection.jsx │ │ ├── SarcomaLabsSection.jsx │ │ ├── SarcomaMetadata.jsx │ │ ├── SarcomaNursePractitionerMetadata.jsx │ │ ├── SarcomaPatientSummarySection.jsx │ │ ├── SarcomaSummarySection.jsx │ │ ├── TemperatureSubsection.jsx │ │ ├── TimelineSection.jsx │ │ ├── TreatmentOptionsSection.jsx │ │ ├── VisitReasonPostEncounterSection.jsx │ │ ├── VisitReasonPreEncounterSection.jsx │ │ ├── VitalsSection.jsx │ │ ├── VitalsSubsection.jsx │ │ ├── WeightSubsection.jsx │ │ └── WhiteBloodCellCountSubsection.jsx ├── templates │ ├── TemplateOption.jsx │ ├── TemplateOption.scss │ ├── TemplateOptionPreviewButton.jsx │ ├── TemplateOptionPreviewButton.scss │ ├── TemplateSelectionView.jsx │ └── TemplateSelectionView.scss ├── timeline │ ├── HoverItem.css │ ├── HoverItem.jsx │ ├── Item.jsx │ ├── Timeline.scss │ ├── TimelineEventsVisualizer.jsx │ ├── TimelineEventsVisualizer.scss │ ├── TimelineLegend.css │ ├── TimelineLegend.jsx │ └── util.js └── viewer │ ├── ShortcutViewer.css │ └── ShortcutViewer.jsx └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | BROWSER=http://localhost:3000/pilot1 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | src/lib/slate/* 2 | src/lib/slate-suggestions-dist/* 3 | src/lib/react-minimap/* 4 | src/model/* 5 | src/dataaccess/mcodev0.1-datasource/model/* 6 | src/__test__/* 7 | src/lib/cql-execution/* 8 | src/lib/FHIRMapper.js 9 | src/mcode-pilot/mock-data/mock-data.js 10 | src/summary/TreatmentData.js 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "react-app", 3 | "rules": { 4 | "arrow-spacing": "warn", 5 | "block-spacing": "warn", 6 | "key-spacing": "warn", 7 | "keyword-spacing": "warn", 8 | "indent": ["warn", 4, { "SwitchCase": 1 }], 9 | "no-trailing-spaces": "warn", 10 | "no-var": "warn", 11 | "prefer-const": "warn", 12 | "semi": "warn", 13 | "semi-spacing": "warn", 14 | "space-before-blocks": "warn", 15 | "space-in-parens": "warn" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 10.14.1 5 | 6 | script: 7 | - yarn build-css && yarn test-ci 8 | - yarn build 9 | 10 | notifications: 11 | email: 12 | on_failure: change 13 | 14 | after_success: 'npm run coveralls' 15 | 16 | cache: 17 | yarn: true 18 | directories: 19 | - node_modules 20 | -------------------------------------------------------------------------------- /config-overrides.js: -------------------------------------------------------------------------------- 1 | const CopyWebpackPlugin = require('copy-webpack-plugin'); 2 | const { 3 | override, 4 | useEslintRc, 5 | addWebpackExternals, 6 | addWebpackPlugin 7 | } = require("customize-cra"); 8 | 9 | 10 | module.exports = override( 11 | useEslintRc(), 12 | addWebpackExternals({ 13 | 'fhir-mapper': "Mapper", 14 | 'babel-polyfill': "_babelPolyfill" 15 | }), 16 | addWebpackPlugin(new CopyWebpackPlugin([ 17 | { context:'node_modules/fhir-mapper/dist/', from: 'app.bundle.js*', to: 'static/js/fhir-mapper' } 18 | ])) 19 | ); 20 | -------------------------------------------------------------------------------- /design/readme.md: -------------------------------------------------------------------------------- 1 | # FLUX NOTES DESIGNS 2 | 3 | Design files associated with Flux Notes have been uploaded periodically throughout the project (workflow diagrams, UI mockups, interaction concepts, among other deliverables). To view them, please go to the following link: 4 | https://github.com/standardhealth/shr_design/tree/master/Flux%20Notes 5 | -------------------------------------------------------------------------------- /design/shr_fluxnotes_concept_v11.3_screens_components_v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/design/shr_fluxnotes_concept_v11.3_screens_components_v1.pdf -------------------------------------------------------------------------------- /docs/FluxNotesManual_v0.1.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/docs/FluxNotesManual_v0.1.docx -------------------------------------------------------------------------------- /docs/Structured Phrase Format Descriptions.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/docs/Structured Phrase Format Descriptions.pptx -------------------------------------------------------------------------------- /docs/fluxArchitecture.vpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/docs/fluxArchitecture.vpp -------------------------------------------------------------------------------- /public/DebraHernandez672.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/DebraHernandez672.jpg -------------------------------------------------------------------------------- /public/DebraHernandez672.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/DebraHernandez672.png -------------------------------------------------------------------------------- /public/EllaOrtiz111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/EllaOrtiz111.png -------------------------------------------------------------------------------- /public/JaneBradshaw146.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/JaneBradshaw146.png -------------------------------------------------------------------------------- /public/ServerConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseURL": "http://localhost:3001/api" 3 | } -------------------------------------------------------------------------------- /public/WesWelker83.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/WesWelker83.png -------------------------------------------------------------------------------- /public/clinicalTrialEnrollmentSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/clinicalTrialEnrollmentSheet.pdf -------------------------------------------------------------------------------- /public/clinicalTrialUnenrolledSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/clinicalTrialUnenrolledSheet.pdf -------------------------------------------------------------------------------- /public/compass-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/compass-logo.png -------------------------------------------------------------------------------- /public/deceasedSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/deceasedSheet.pdf -------------------------------------------------------------------------------- /public/diseaseStatusSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/diseaseStatusSheet.pdf -------------------------------------------------------------------------------- /public/fluxnotes_logo_b&w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/fluxnotes_logo_b&w.png -------------------------------------------------------------------------------- /public/fluxnotes_logo_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/fluxnotes_logo_color.png -------------------------------------------------------------------------------- /public/icare_logo_b&w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/icare_logo_b&w.png -------------------------------------------------------------------------------- /public/icare_logo_color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/icare_logo_color.png -------------------------------------------------------------------------------- /public/icons/angled-pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | angled pencil 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/icons/icon_arrow_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Shape 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/icons/icon_view_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/icons/icon_view_left.png -------------------------------------------------------------------------------- /public/icons/icon_view_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 2 Copy 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/icons/icon_view_middle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/icons/icon_view_middle.png -------------------------------------------------------------------------------- /public/icons/icon_view_middle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 18 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/icons/icon_view_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/icons/icon_view_right.png -------------------------------------------------------------------------------- /public/icons/icon_view_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 19 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /public/landing/fluxnotes_compass.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/fluxnotes_compass.gif -------------------------------------------------------------------------------- /public/landing/fluxnotes_full.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/fluxnotes_full.gif -------------------------------------------------------------------------------- /public/landing/fluxnotes_gif_fullSize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/fluxnotes_gif_fullSize.gif -------------------------------------------------------------------------------- /public/landing/fluxnotes_poc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/fluxnotes_poc.gif -------------------------------------------------------------------------------- /public/landing/grid_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/grid_background.png -------------------------------------------------------------------------------- /public/landing/icon_efficacy_research.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_efficacy_research.png -------------------------------------------------------------------------------- /public/landing/icon_market_surveillance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_market_surveillance.png -------------------------------------------------------------------------------- /public/landing/icon_provider_burden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_provider_burden.png -------------------------------------------------------------------------------- /public/landing/icon_rare_diseases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_rare_diseases.png -------------------------------------------------------------------------------- /public/landing/icon_regimen_analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_regimen_analysis.png -------------------------------------------------------------------------------- /public/landing/icon_utilization.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/icon_utilization.png -------------------------------------------------------------------------------- /public/landing/img_fluxnotes_lite.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/landing/img_fluxnotes_lite.jpg -------------------------------------------------------------------------------- /public/logos/ASCO.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/ASCO.png -------------------------------------------------------------------------------- /public/logos/Alliance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/Alliance.png -------------------------------------------------------------------------------- /public/logos/BWH.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/BWH.png -------------------------------------------------------------------------------- /public/logos/Browserstack-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/Browserstack-logo.png -------------------------------------------------------------------------------- /public/logos/DanaFarber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/DanaFarber.png -------------------------------------------------------------------------------- /public/logos/FluxNotesLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/FluxNotesLogo.png -------------------------------------------------------------------------------- /public/logos/ICARE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/ICARE.png -------------------------------------------------------------------------------- /public/logos/MITRE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/MITRE.png -------------------------------------------------------------------------------- /public/logos/SHR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/SHR.png -------------------------------------------------------------------------------- /public/logos/codex-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/codex-Logo.png -------------------------------------------------------------------------------- /public/logos/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/favicon-16x16.png -------------------------------------------------------------------------------- /public/logos/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/favicon-32x32.png -------------------------------------------------------------------------------- /public/logos/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/favicon.ico -------------------------------------------------------------------------------- /public/logos/mCODE-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/logos/mCODE-Logo.png -------------------------------------------------------------------------------- /public/mapper.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/mapper.js -------------------------------------------------------------------------------- /public/pathology-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/pathology-report.pdf -------------------------------------------------------------------------------- /public/spa.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><% 2 | response.setStatus(200); 3 | %><%@include file="./index.html"%> 4 | -------------------------------------------------------------------------------- /public/stagingSheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/stagingSheet.pdf -------------------------------------------------------------------------------- /public/toxicitySheet.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/public/toxicitySheet.pdf -------------------------------------------------------------------------------- /scripts/check-version.js: -------------------------------------------------------------------------------- 1 | const LTS_VERSION = "v10.14.1" 2 | 3 | if (process.version !== LTS_VERSION) { 4 | console.log(` 5 | ********** 6 | * WARNING: Your version of node doesn't match our LTS: Expected ${LTS_VERSION}, got ${process.version} 7 | ********** 8 | `); 9 | } else { 10 | console.log("Version of Node checks out"); 11 | } 12 | -------------------------------------------------------------------------------- /src/__test__/backend/config/ConfigManager.test.js: -------------------------------------------------------------------------------- 1 | import ConfigManager from '../../../config/ConfigManager'; 2 | import {expect} from 'chai'; 3 | const nock = require('nock'); 4 | 5 | describe("ConfigManager", function () { 6 | it('should aquire defaults from global.CONFIG object if available', function () { 7 | global.CONFIG = {foo:"bar"}; 8 | let cm = new ConfigManager(); 9 | 10 | expect(cm.config) 11 | .to.deep.equal(global.CONFIG); 12 | }); 13 | 14 | it('should be able to load a config file from a url', function () { 15 | let config = {foo: 'BAR'}; 16 | const scope = nock('http://localhost/') 17 | .get('/config.json') 18 | .reply(200, config); 19 | 20 | global.CONFIG = {}; 21 | let cm = new ConfigManager(); 22 | expect(cm.config) 23 | .to.deep.equal(global.CONFIG); 24 | cm.loadConfiguration("/config.json").then(()=> { 25 | expect(cm.config) 26 | .to.deep.equal(config); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/__test__/backend/dashboard/DashboardManager.test.js: -------------------------------------------------------------------------------- 1 | import DashboardManager from '../../../dashboard/DashboardManager'; 2 | import {expect} from 'chai' 3 | 4 | // Create one das 5 | const emptyDashboardManager = new DashboardManager([]); 6 | const dashboardManager = new DashboardManager(); 7 | 8 | describe("getPossibleSuperRoles", function () { 9 | it('should return an empty list if there are no defined superRoles', function () { 10 | const allRoles = emptyDashboardManager.getPossibleSuperRoles(); 11 | expect(allRoles) 12 | .to.be.an("array") 13 | .and.to.be.empty; 14 | }); 15 | 16 | it('should return a list containing Clinician and Patient as default superRoles', function () { 17 | const expectedRoles = ['Clinician', 'Patient']; 18 | const actualRoles = dashboardManager.getPossibleSuperRoles(); 19 | expect(actualRoles) 20 | .to.have.same.members(expectedRoles); 21 | }) 22 | }); -------------------------------------------------------------------------------- /src/__test__/backend/lib/cql-execution/CQLExecutionEngine.test.js: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import * as CQLExecutionEngine from '../../../../lib/cql-execution/CQLExecutionEngine.js'; 3 | import PALLAScql from '../../../../lib/cql-execution/example/cql/PALLASEligibility.json'; 4 | import PALLAS_eligiblePatient from '../../../../lib/cql-execution/example/patients/PALLASPatient.json'; 5 | import PALLAS_ineligiblePatient from '../../../../lib/cql-execution/example/patients/PATINAPatient.json'; 6 | 7 | 8 | describe('getPALLASeligibility', function () { 9 | it('should return a boolean - true if FHIR patient passes PALLAS eligibility criteria', function () { 10 | const result = CQLExecutionEngine.getCQLResults(PALLAScql, [PALLAS_eligiblePatient, PALLAS_ineligiblePatient]); 11 | expect(result.patientResults['93d8b432-f097-4c82-b111-8577e1d9d89f'].meetsInclusionCriteria) 12 | .to.be.true; 13 | expect(result.patientResults['3cb09ecb-e927-4946-82b3-89957e193215'].meetsInclusionCriteria) 14 | .to.be.false; 15 | 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/__test__/backend/mcode/services/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "demographics": { 3 | "gender": { 4 | "codeSystem": "SNOMEDCT", 5 | "displayName": "Female", 6 | "code": "703118005" 7 | }, 8 | "age": { 9 | "min": 43, 10 | "max": 63 11 | }, 12 | "age_at_diagnosis": { 13 | "min": 42, 14 | "max": 62 15 | }, 16 | "race": { 17 | "codeSystemName": "HL7 v3 Code System Race", 18 | "codeSystem": "2.16.840.1.113883.5.104", 19 | "displayName": "White", 20 | "code" : "2106-3" 21 | } 22 | 23 | }, 24 | "diagnosis" : { "stage": "IA", "grade": 3, "tnm": { "t": "T1c", "m": "M0", 25 | "n" : "N0" } }, 26 | "tumorMarkers": [{ 27 | "code": "16112-5", 28 | "codeSystem": "http://fhir.loinc.org", 29 | "displayName": "Estrogen Receptor", 30 | "value": "Positive" 31 | }, 32 | { 33 | "code": "16113-3", 34 | "codeSystem": "http://fhir.loinc.org", 35 | "displayName": "Progesterone Receptor", 36 | "value": "Negative" 37 | }, 38 | { 39 | "code": "48676-1", 40 | "codeSystem": "http://fhir.loinc.org", 41 | "displayName": "HER2 Receptor", 42 | "value": "Positive" 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /src/__test__/backend/mcode/services/rows.js: -------------------------------------------------------------------------------- 1 | export default [{ 2 | "id": "row_1", 3 | "displayName": "A & B", 4 | "treatments": [{ 5 | "code": "A", 6 | "displayName": "A", 7 | "codeSystem": "2.16.840.1.113883.6.88", 8 | "codeSystemName": "RXNORM" 9 | }, { 10 | "code": "B", 11 | "displayName": "B", 12 | "codeSystem": "2.16.840.1.113883.6.88", 13 | "codeSystemName": "RXNORM" 14 | }], 15 | "totalPatients": 2039, 16 | "survivorsPerYear": [,100,,99,,98], 17 | "sideEffects": { 18 | "totalReporting": 0, 19 | "effects": {} 20 | } 21 | }, { 22 | "id": "row_2", 23 | "displayName": "A", 24 | "treatments": [{ 25 | "code": "A", 26 | "displayName": "A", 27 | "codeSystem": "2.16.840.1.113883.6.88", 28 | "codeSystemName": "RXNORM" 29 | }], 30 | "totalPatients": 4028, 31 | "survivorsPerYear": [,200, ,201,,200], 32 | "sideEffects": { 33 | "totalReporting": 0, 34 | "effects": {} 35 | } 36 | }] -------------------------------------------------------------------------------- /src/__test__/backend/model/CodeableConceptUtils.test.js: -------------------------------------------------------------------------------- 1 | import {expect} from 'chai'; 2 | import * as codeableConceptUtils from '../../../model/CodeableConceptUtils'; 3 | 4 | describe('getCodeableConceptFromTuple', function() { 5 | const tuple = { 6 | value: 'test', 7 | codeSystem: '1234', 8 | displayText: 'display!' 9 | }; 10 | const concept = codeableConceptUtils.getCodeableConceptFromTuple(tuple); 11 | 12 | it('should return CodeableConcept object with corresponding code, codeySystem, and displayText', function() { 13 | expect(concept) 14 | .to.be.an('object'); 15 | expect(concept.displayText.value) 16 | .to.be.a('string') 17 | .eql(tuple.displayText); 18 | expect(concept.coding) 19 | .to.be.an('array') 20 | .that.is.not.empty; 21 | expect(concept.coding[0].codeValue.code) 22 | .to.be.a('string') 23 | .eql(tuple.value); 24 | expect(concept.coding[0].codeSystem.value) 25 | .to.be.a('string') 26 | .eql(tuple.codeSystem); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/__test__/backend/model/actor/FluxDeceased.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxDeceased from '../../../../model/actor/FluxDeceased'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // let deceased = new FluxDeceased(); 6 | // deceased.dateOfDeath = '11 Nov 2017'; 7 | 8 | // describe('getDateOfDeath()', function() { 9 | // it('should return "11 Nov 2017"', function() { 10 | // expect(deceased.dateOfDeath) 11 | // .to.be.a('string') 12 | // .eql('11 Nov 2017'); 13 | // }); 14 | // }); 15 | 16 | // describe('setDateOfDeath()', function() { 17 | // it('should set dateOfDeath to "1 Oct 2017"', function() { 18 | // deceased.dateOfDeath = '1 Oct 2017'; 19 | // expect(deceased.dateOfDeath) 20 | // .to.be.a('string') 21 | // .eql('1 Oct 2017'); 22 | // }); 23 | // }); -------------------------------------------------------------------------------- /src/__test__/backend/model/oncology/FluxEstrogenReceptorStatus.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxEstrogenReceptorStatus from '../../../../model/oncology/FluxEstrogenReceptorStatus'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // let erstatus = new FluxEstrogenReceptorStatus(); 6 | // erstatus.status = 'Positive'; 7 | 8 | // describe('getStatus()', function() { 9 | // it('should return "Positive"', function() { 10 | // expect(erstatus.status) 11 | // .to.be.a('string') 12 | // .eql('Positive'); 13 | // }); 14 | // }); 15 | 16 | // describe('setStatus()', function() { 17 | // it('should set status to "Negative"', function() { 18 | // erstatus.status = 'Negative'; 19 | // expect(erstatus.status) 20 | // .to.be.a('string') 21 | // .eql('Negative'); 22 | // }); 23 | // }); -------------------------------------------------------------------------------- /src/__test__/backend/model/oncology/FluxHistologicGrade.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxHistologicGrade from '../../../../model/oncology/FluxHistologicGrade'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // const gradeJson = { 6 | // "entryType": [ "http://standardhealthrecord.org/oncology/HistologicGrade", 7 | // "http://standardhealthrecord.org/observation/Observation", 8 | // "http://standardhealthrecord.org/base/Action" ], 9 | // "value": {"coding": [{"value": "369792005", "codeSystem": {"value":"http://snomed.info/sct"}, "displayText": "High grade or poorly differentiated"}]}, 10 | // "status": "final", 11 | // "_issue": "stricly speaking, the observations in this list are elements and not entries so entryType shouldn't exist but how would you know which concrete type this represents otherwise? Problem is bigger for choices where some or all choices are just elements" 12 | // }; 13 | 14 | // let grade = new FluxHistologicGrade(gradeJson); 15 | 16 | // describe('getGrade()', function() { 17 | // it('should return "High grade or poorly differentiated"', function() { 18 | // expect(grade.grade) 19 | // .to.be.a('string') 20 | // .eql('High grade or poorly differentiated'); 21 | // }); 22 | // }); 23 | -------------------------------------------------------------------------------- /src/__test__/backend/model/oncology/FluxHumanEpiduralGrowthFactorReceptor2Status.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxHumanEpiduralGrowthFactorReceptor2Status from '../../../../model/oncology/FluxHumanEpiduralGrowthFactorReceptor2Status'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // let her2status = new FluxHumanEpiduralGrowthFactorReceptor2Status(); 6 | // her2status.status = 'Positive'; 7 | 8 | // describe('getStatus()', function() { 9 | // it('should return "Positive"', function() { 10 | // expect(her2status.status) 11 | // .to.be.a('string') 12 | // .eql('Positive'); 13 | // }); 14 | // }); 15 | 16 | // describe('setStatus()', function() { 17 | // it('should set status to "Negative"', function() { 18 | // her2status.status = 'Negative'; 19 | // expect(her2status.status) 20 | // .to.be.a('string') 21 | // .eql('Negative'); 22 | // }); 23 | // }); -------------------------------------------------------------------------------- /src/__test__/backend/model/oncology/FluxProgesteroneReceptorStatus.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxProgesteroneReceptorStatus from '../../../../model/oncology/FluxProgesteroneReceptorStatus'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // let prstatus = new FluxProgesteroneReceptorStatus(); 6 | // prstatus.status = 'Positive'; 7 | 8 | // describe('getStatus()', function() { 9 | // it('should return "Positive"', function() { 10 | // expect(prstatus.status) 11 | // .to.be.a('string') 12 | // .eql('Positive'); 13 | // }); 14 | // }); 15 | 16 | // describe('setStatus()', function() { 17 | // it('should set status to "Negative"', function() { 18 | // prstatus.status = 'Negative'; 19 | // expect(prstatus.status) 20 | // .to.be.a('string') 21 | // .eql('Negative'); 22 | // }); 23 | // }); -------------------------------------------------------------------------------- /src/__test__/backend/model/oncology/FluxTumorSize.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxTumorSize from '../../../../model/oncology/FluxTumorSize'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // const tumorJson = { 6 | // "entryType": [ "http://standardhealthrecord.org/oncology/TumorSize", 7 | // "http://standardhealthrecord.org/observation/Observation", 8 | // "http://standardhealthrecord.org/base/Action" ], 9 | // "value": {"value": 30.0, "units": {"value": "mm"}}, 10 | // "specificType": {"value": {"coding": [{"value": "C0475440", "codeSystem": {"value":"http://ncimeta.nci.nih.gov"}, "displayText": "Tumor Size"}]}}, 11 | // "status": "final", 12 | // "_issue": "strictly speaking, the observations in this list are elements and not entries so entryType shouldn't exist but how would you know which concrete type this represents otherwise? Problem is bigger for choices where some or all choices are just elements" 13 | // }; 14 | 15 | // let tumorSize = new FluxTumorSize(tumorJson); 16 | 17 | // describe('getQuantity()', function() { 18 | // it('should return an object with value and unit', function() { 19 | // const expectedQuantity = { 20 | // value: 30.0, 21 | // unit: "mm" 22 | // }; 23 | // expect(tumorSize.quantity) 24 | // .to.be.a('object') 25 | // .eql(expectedQuantity); 26 | // }); 27 | // }); 28 | -------------------------------------------------------------------------------- /src/__test__/backend/model/research/FluxStudy.disabled.js: -------------------------------------------------------------------------------- 1 | // import FluxStudy from '../../../../model/research/FluxStudy'; 2 | // import {expect} from 'chai'; 3 | // import util from 'util'; 4 | 5 | // let study = new FluxStudy(); 6 | // study.title = 'PATINA'; 7 | // study.identifier = '123'; 8 | 9 | // describe('getTitle()', function() { 10 | // it('should return "PATINA"', function() { 11 | // expect(study.title) 12 | // .to.be.a('string') 13 | // .eql('PATINA'); 14 | // }); 15 | // }); 16 | 17 | // describe('setTitle()', function() { 18 | // it('should set title to "ICARE"', function() { 19 | // study.title = 'ICARE'; 20 | // expect(study.title) 21 | // .to.be.a('string') 22 | // .eql('ICARE'); 23 | // }); 24 | // }); 25 | 26 | // describe('getIdentifier()', function() { 27 | // it('should return "123"', function() { 28 | // expect(study.identifier) 29 | // .to.be.a('string') 30 | // .eql('123'); 31 | // }); 32 | // }); 33 | 34 | // describe('setIdentifier()', function() { 35 | // it('should set title to "235"', function() { 36 | // study.identifier = '235'; 37 | // expect(study.identifier) 38 | // .to.be.a('string') 39 | // .eql('235'); 40 | // }); 41 | // }); -------------------------------------------------------------------------------- /src/__test__/backend/patient/FakeDataElement.js: -------------------------------------------------------------------------------- 1 | /** Fake Data Element */ 2 | class FakeDataElement { 3 | 4 | get value() { 5 | return this.something; 6 | } 7 | 8 | set value(val) { 9 | this.something = val; 10 | } 11 | } 12 | 13 | export default FakeDataElement; 14 | -------------------------------------------------------------------------------- /src/__test__/backend/patientControl/FakePatient.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/src/__test__/backend/patientControl/FakePatient.js -------------------------------------------------------------------------------- /src/__test__/backend/views/Landing.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Enzyme, { shallow } from 'enzyme'; 3 | import Adapter from 'enzyme-adapter-react-15'; 4 | import { expect } from 'chai'; 5 | 6 | import LandingPage from '../../../components/LandingPage'; 7 | 8 | Enzyme.configure({ adapter: new Adapter() }); 9 | 10 | describe('Landing', function() { 11 | it('Can navigate to pages', () => { 12 | const wrapper = shallow(); 13 | const fullAppDiv = wrapper.find('#link-to-full'); 14 | expect(fullAppDiv.prop('href')).to.equal('/pilot1') 15 | }); 16 | }); -------------------------------------------------------------------------------- /src/__test__/testHelper.js: -------------------------------------------------------------------------------- 1 | import {ConfigManagerInstance} from '../config/ConfigManager'; 2 | import fs from 'fs'; 3 | const JSON5 = require('json5'); 4 | const data = new String(fs.readFileSync('./public/config.js')); 5 | const config = JSON5.parse(data.substring(data.indexOf("{")-1)); 6 | ConfigManagerInstance.addConfiguration(config); 7 | -------------------------------------------------------------------------------- /src/actions/types.js: -------------------------------------------------------------------------------- 1 | // ------------------------- MCODE ----------------------------------------- // 2 | 3 | export const INITIALIZE_SIMILAR_PATIENT_PROPS = 'INITIALIZE_SIMILAR_PATIENT_PROPS'; 4 | export const SELECT_ALL_CATEGORY_SIMILAR_PATIENT_OPTIONS = 'SELECT_ALL_CATEGORY_SIMILAR_PATIENT_OPTIONS'; 5 | export const SELECT_ALL_SIMILAR_PATIENT_OPTIONS = 'SELECT_ALL_SIMILAR_PATIENT_OPTIONS'; 6 | export const SELECT_SIMILAR_PATIENT_OPTION = 'SELECT_SIMILAR_PATIENT_OPTION'; 7 | export const SELECT_SIMILAR_PATIENT_OPTION_RANGE = 'SELECT_SIMILAR_PATIENT_OPTION_RANGE'; 8 | export const SELECT_TREATMENTS = 'SELECT_TREATMENTS'; 9 | export const UPDATE_PATIENT_OUTCOMES = 'UPDATE_PATIENT_OUTCOMES'; 10 | export const SET_SELECTED_TREATMENT = 'SET_SELECTED_TREATMENT'; 11 | export const SET_SELECTED_SIDE_EFFECTS = 'SET_SELECTED_SIDE_EFFECTS'; 12 | -------------------------------------------------------------------------------- /src/apps/AppManager.jsx: -------------------------------------------------------------------------------- 1 | import { CompassApp } from '../containers/CompassApp'; 2 | import SmartCompassApp from '../containers/SmartCompassApp'; 3 | import { FullApp } from '../containers/FullApp'; 4 | import SmartFullApp from '../containers/SmartFullApp'; 5 | import SlimApp from '../containers/SlimApp'; 6 | import PointOfCareApp from '../containers/PointOfCareApp'; 7 | import Pilot2MvpApp from '../containers/Pilot2MvpApp'; 8 | import SmartPilot2MvpApp from '../containers/SmartPilot2MvpApp'; 9 | import LandingPage from '../components/LandingPage'; 10 | import LaunchPage from '../components/LaunchPage'; 11 | import {ConfigManagerInstance} from '../config/ConfigManager'; 12 | 13 | const APPS = { 14 | 'SlimApp': SlimApp, 15 | 'FullApp': FullApp, 16 | 'SmartFullApp': SmartFullApp, 17 | 'LaunchPage': LaunchPage, 18 | 'LandingPage': LandingPage, 19 | 'CompassApp': CompassApp, 20 | 'SmartCompassApp': SmartCompassApp, 21 | 'PointOfCareApp': PointOfCareApp, 22 | 'Pilot2MvpApp': Pilot2MvpApp, 23 | 'SmartPilot2MvpApp': SmartPilot2MvpApp, 24 | }; 25 | 26 | export default class AppManager { 27 | constructor() { 28 | this.apps = ConfigManagerInstance.get('apps').map((appConfig) => { 29 | appConfig.app = APPS[appConfig.app]; 30 | return appConfig; 31 | }); 32 | } 33 | getSupportedApps() { 34 | return this.apps; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/components/WithTracker.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ReactGA from 'react-ga'; 4 | 5 | ReactGA.initialize('UA-82650858-3'); 6 | 7 | const WithTracker = (WrappedComponent, options = {}) => { 8 | const trackPage = page => { 9 | ReactGA.set({ 10 | page, 11 | ...options, 12 | }); 13 | ReactGA.pageview(page); 14 | }; 15 | 16 | const HOC = class extends Component { 17 | componentDidMount() { 18 | const page = this.props.location.pathname; 19 | trackPage(page); 20 | } 21 | 22 | componentWillReceiveProps(nextProps) { 23 | const currentPage = this.props.location.pathname; 24 | const nextPage = nextProps.location.pathname; 25 | 26 | if (currentPage !== nextPage) { 27 | trackPage(nextPage); 28 | } 29 | } 30 | 31 | render() { 32 | return ; 33 | } 34 | }; 35 | 36 | HOC.propTypes = { 37 | path: PropTypes.string.isRequired, 38 | location: PropTypes.object.isRequired, 39 | display: PropTypes.string.isRequired, 40 | // App is a react component, which is itself a function 41 | app: PropTypes.func.isRequired, 42 | isExact: PropTypes.bool.isRequired 43 | }; 44 | 45 | return HOC; 46 | }; 47 | 48 | export default WithTracker; 49 | -------------------------------------------------------------------------------- /src/config/ConfigManager.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default class ConfigManager { 4 | constructor(config = {}) { 5 | this.config = global.CONFIG ? { ...global.CONFIG , ...config } : { ...config }; 6 | } 7 | 8 | addConfiguration(conf) { 9 | if (conf) { 10 | this.config = { 11 | ...this.config, 12 | ...conf 13 | }; 14 | } 15 | } 16 | 17 | async loadConfiguration(url) { 18 | const response = await axios.get(url); 19 | if (response) { 20 | this.addConfiguration(response.data); 21 | } 22 | } 23 | 24 | get(name, defaultValue) { 25 | const parts = name.split('.'); 26 | let context = this.config; 27 | let value = null; 28 | for (const i in parts) { 29 | const part = parts[i]; 30 | value = context[part]; 31 | context = value; 32 | } 33 | return value || defaultValue; 34 | } 35 | } 36 | 37 | const instance = new ConfigManager(); 38 | export { instance as ConfigManagerInstance }; 39 | -------------------------------------------------------------------------------- /src/config/ServiceManager.js: -------------------------------------------------------------------------------- 1 | import { ConfigManagerInstance } from './ConfigManager'; 2 | import StaticOutcomesService from '../mcode-pilot/services/outcomes/StaticOutcomesService'; 3 | import CLQOutcomesService from '../mcode-pilot/services/outcomes/CLQOutcomesService'; 4 | 5 | const SERVICES = { 6 | 'StaticOutcomesService': StaticOutcomesService, 7 | 'CLQOutcomesService': CLQOutcomesService 8 | }; 9 | 10 | export default class ServiceManager { 11 | constructor() { 12 | this.services = ConfigManagerInstance.get('services', {}); 13 | const keys = Object.keys(this.services); 14 | for (const key of keys) { 15 | const serviceConfig = this.services[key]; 16 | if (typeof serviceConfig === 'string') { 17 | const type = SERVICES[serviceConfig]; 18 | this.services[key] = new type(); 19 | } else if (serviceConfig && serviceConfig.name) { 20 | const type = SERVICES[serviceConfig.name]; 21 | this.services[key] = new type(serviceConfig); 22 | } 23 | } 24 | } 25 | 26 | getService(name) { 27 | return this.services[name]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/containers/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import 'fhirclient'; 4 | import AppManager from '../apps/AppManager'; 5 | import WithTracker from '../components/WithTracker'; 6 | 7 | const appManager = new AppManager(); 8 | const apps = appManager.getSupportedApps(); 9 | 10 | 11 | const App = () => ( 12 |
13 | {apps.map((appObject, i) => 14 | { 15 | return React.createElement(WithTracker(appObject.app), { ...props, ...appObject }); 16 | }} key={i}/> 17 | )} 18 |
19 | ); 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/containers/SmartCompassApp.jsx: -------------------------------------------------------------------------------- 1 | import { CompassApp } from "./CompassApp"; 2 | import WithSmartData from './WithSmartData'; 3 | 4 | const SmartCompassApp = WithSmartData(CompassApp); 5 | export default SmartCompassApp; -------------------------------------------------------------------------------- /src/containers/SmartFullApp.jsx: -------------------------------------------------------------------------------- 1 | import { FullApp } from "./FullApp"; 2 | import WithSmartData from './WithSmartData'; 3 | 4 | const SmartFullApp = WithSmartData(FullApp); 5 | export default SmartFullApp; -------------------------------------------------------------------------------- /src/containers/SmartPilot2MvpApp.jsx: -------------------------------------------------------------------------------- 1 | import { Pilot2MvpApp } from "./Pilot2MvpApp"; 2 | import WithSmartData from './WithSmartData'; 3 | 4 | const SmartPilot2MvpApp = WithSmartData(Pilot2MvpApp); 5 | export default SmartPilot2MvpApp; 6 | -------------------------------------------------------------------------------- /src/context/ContextGetHelp.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | ul.context-get-help { 4 | max-height: 250px; 5 | overflow-y: auto; 6 | overflow-x: hidden; 7 | list-style-type: none; 8 | padding: 0; 9 | margin: 0; 10 | background-color: $background; 11 | 12 | li { 13 | padding: 5px 10px; 14 | display: flex; 15 | justify-content: space-between; 16 | & > * { 17 | display: block; 18 | } 19 | } 20 | 21 | li[data-active="true"] { 22 | background-color: $interface-blue; 23 | color: $background; 24 | } 25 | 26 | .context-get-help-li { 27 | margin-top: 5px; 28 | margin-bottom: 5px; 29 | 30 | .context-get-help-text { 31 | span.fa-angle-up, span.fa-angle-down { 32 | padding: 0; 33 | padding-left: 15px; 34 | } 35 | } 36 | .context-information-text { 37 | span.fa-info-circle { 38 | padding: 0 5px 0 0; 39 | } 40 | } 41 | } 42 | 43 | .context-get-help-options { 44 | li:last-child { 45 | margin-bottom: 5px; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/context/ContextItem.scss: -------------------------------------------------------------------------------- 1 | @import "../styles/variables"; 2 | 3 | .context-menu-item { 4 | padding: 5px 10px; 5 | width: 300px; 6 | display: flex; 7 | justify-content: space-between; 8 | & > * { 9 | display:block; 10 | } 11 | } 12 | 13 | .context-menu-item[data-active="true"] { 14 | background-color: $interface-blue; 15 | color: #fff; 16 | } 17 | 18 | .right-aligned { 19 | padding-left: 15px; 20 | } -------------------------------------------------------------------------------- /src/context/ContextListOptions.scss: -------------------------------------------------------------------------------- 1 | ul.context-list-options { 2 | max-height: 250px; 3 | overflow-y: auto; 4 | overflow-x: hidden; 5 | list-style-type: none; 6 | padding: 0; 7 | margin: 0; 8 | background-color: #fff; 9 | li:first-child { 10 | margin-top: 10px; 11 | } 12 | li:last-child { 13 | margin-bottom: 10px; 14 | } 15 | } -------------------------------------------------------------------------------- /src/context/ContextOptions.scss: -------------------------------------------------------------------------------- 1 | @import "../styles/variables"; 2 | 3 | .context-option { 4 | margin: 1px 0; 5 | cursor: pointer; 6 | font-size: $size-m; 7 | color: $body-gray; 8 | } 9 | 10 | .context-option:last-of-type { 11 | margin-bottom: 0; 12 | } 13 | 14 | .context-option:hover { 15 | color: $body-black; 16 | font-weight: $weight-semibold; 17 | } 18 | 19 | .context-options-section { 20 | padding-left: $note-assistant-overhang; 21 | } 22 | 23 | .context-options-list { 24 | overflow-y: hidden; 25 | overflow-x: hidden; 26 | font-weight: $weight-regular; 27 | } 28 | 29 | .hidden { 30 | display: none; 31 | } 32 | 33 | /* 34 | * Tooltip Styling 35 | */ 36 | 37 | .context-panel-tooltip{ 38 | .rc-tooltip-inner{ 39 | width: 150px; 40 | min-height: 22px; 41 | text-align: center; 42 | border-radius: 6px; 43 | } 44 | 45 | .large .rc-tooltip-inner{ 46 | width: 220px; 47 | } 48 | } 49 | 50 | .context-options-header { 51 | margin-top: $size-s; 52 | font-style: italic; 53 | font-size: $size-m; 54 | color: $state; 55 | } 56 | -------------------------------------------------------------------------------- /src/context/ContextTray.scss: -------------------------------------------------------------------------------- 1 | @import "../styles/variables"; 2 | 3 | 4 | .context-tray { 5 | margin: 5px 0px 20px 0; 6 | color: $body-gray; 7 | 8 | .view-mode-section-item { 9 | margin: 3px 0; 10 | font-size: $size-m; 11 | color: $state; 12 | cursor: pointer; 13 | text-overflow: ellipsis; 14 | overflow: hidden; 15 | white-space: nowrap; 16 | } 17 | 18 | .view-mode-section-item:hover, 19 | .view-mode-section-item.selected { 20 | font-weight: $weight-semibold; 21 | color: $body-gray; 22 | } 23 | 24 | .view-mode-section-menu { 25 | padding: 5px 0px 5px 0px+$note-assistant-overhang; 26 | border-bottom: 1px solid $line-gray; 27 | } 28 | 29 | .view-mode-section-item-disabled { 30 | margin: 3px 0px 3px 26px; 31 | font-size: $size-m; 32 | text-overflow: ellipsis; 33 | overflow: hidden; 34 | white-space: nowrap; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/context/EditorPortal.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables.scss'; 2 | 3 | .completion-portal { 4 | box-sizing: border-box; 5 | border-radius: 2px; 6 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 7 | box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px; 8 | transition: all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms; 9 | margin-top: 30px; 10 | z-index: 1111; 11 | background-color: $background; 12 | opacity: 1; 13 | } 14 | -------------------------------------------------------------------------------- /src/context/PatientContext.jsx: -------------------------------------------------------------------------------- 1 | import Context from './Context'; 2 | /*import NameInserter from '../shortcuts/NameInserter'; 3 | import DateOfBirthInserter from '../shortcuts/DateOfBirthInserter'; 4 | import AgeInserter from '../shortcuts/AgeInserter'; 5 | import GenderInserter from '../shortcuts/GenderInserter'; 6 | import PatientInserter from '../shortcuts/PatientInserter'; 7 | import ConditionInserter from '../shortcuts/ConditionInserter'; 8 | import DeceasedCreator from '../shortcuts/DeceasedCreator'; 9 | import ClinicalTrialCreator from '../shortcuts/ClinicalTrialCreator'; 10 | */ 11 | 12 | class PatientContext extends Context { 13 | constructor(patient) { 14 | super(); 15 | this.patient = patient; 16 | } 17 | 18 | getValidChildShortcuts(recurse = false) { 19 | /*let result = [ NameInserter, DateOfBirthInserter, AgeInserter, GenderInserter, PatientInserter, ConditionInserter, ClinicalTrialCreator, DeceasedCreator ]; 20 | if (recurse) { 21 | this.getChildren().forEach((subcontext) => { 22 | result = result.concat(subcontext.getValidChildShortcuts(true)); 23 | }); 24 | } 25 | return result;*/ 26 | 27 | } 28 | 29 | getDisplayText() { 30 | return 'Patient'; 31 | } 32 | getId() { 33 | return "Patient"; 34 | } 35 | } 36 | 37 | export default PatientContext; 38 | -------------------------------------------------------------------------------- /src/context/PlaceholderViewModeContent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | export default class PlaceholderViewModeContent extends Component { 5 | handleClick(e, placeholderText) { 6 | const { onClick } = this.props; 7 | 8 | e.preventDefault(); 9 | onClick(placeholderText); 10 | } 11 | 12 | renderPlaceholder(placeholder) { 13 | const placeholderText = `<${placeholder.name}>`; 14 | 15 | return ( 16 |
this.handleClick(e, placeholderText)} 20 | > 21 | {placeholderText} 22 |
23 | ); 24 | } 25 | 26 | render() { 27 | const { placeholders } = this.props; 28 | 29 | return ( 30 |
31 | {placeholders.map((placeholder) => { 32 | return this.renderPlaceholder(placeholder); 33 | })} 34 |
35 | ); 36 | } 37 | } 38 | 39 | PlaceholderViewModeContent.propTypes = { 40 | onClick: PropTypes.func.isRequired, 41 | placeholders: PropTypes.array.isRequired, 42 | }; 43 | -------------------------------------------------------------------------------- /src/context/ShortcutSearch.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import TextField from 'material-ui/TextField'; 4 | 5 | import './ShortcutSearch.css'; 6 | 7 | export default class ShortcutSearch extends Component { 8 | render() { 9 | return ( 10 | 19 | ); 20 | } 21 | } 22 | 23 | ShortcutSearch.propTypes = { 24 | handleSearch: PropTypes.func.isRequired, 25 | }; 26 | -------------------------------------------------------------------------------- /src/context/ShortcutViewModeContent.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables.scss'; 2 | 3 | .condition-contexts, 4 | .section-active { 5 | margin-left: 5px; 6 | } 7 | 8 | #shortcut-search-container { 9 | width: 100%; 10 | padding: 0 $note-assistant-overhang 15px $note-assistant-overhang; 11 | border-bottom: 1px solid $line-gray; 12 | background-color: $background; 13 | } -------------------------------------------------------------------------------- /src/context/SnippetViewModeContent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './SnippetViewModeContent.css'; 4 | 5 | export default class SnippetViewModeContent extends Component { 6 | 7 | // These don't need to be stored as state since they're not being updated 8 | snippets = [ 9 | { name: 'Disease Status', content: '#disease status is #status based on #reasons #as of #date' }, 10 | ]; 11 | 12 | // Insert the content of the template as you would a shortcut 13 | insertSnippet = (snippet) => { 14 | // Insert this snippets content 15 | this.props.onClick(snippet.content); 16 | } 17 | 18 | // Just render the form for the snippets; if additional information was to rendered, we would do it here. 19 | render() { 20 | return ( 21 |
22 | {this.snippets.map((snippet, index) => { 23 | return ( 24 |
this.insertSnippet(snippet)} 28 | > 29 | {snippet.name} 30 |
31 | ); 32 | })} 33 |
34 | ); 35 | } 36 | } 37 | 38 | SnippetViewModeContent.propTypes = { 39 | onClick: PropTypes.func.isRequired, 40 | }; 41 | -------------------------------------------------------------------------------- /src/context/SnippetViewModeContent.scss: -------------------------------------------------------------------------------- 1 | @import "../styles/variables"; 2 | 3 | .snippet { 4 | margin: 5px 0 5px $note-assistant-overhang; 5 | font-size: $size-m; 6 | color: $body-gray; 7 | cursor: pointer; 8 | 9 | &:hover { 10 | color: $body-gray; 11 | font-weight: $weight-semibold; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/dashboard/ClinicianDashboard.css: -------------------------------------------------------------------------------- 1 | #clinician-dashboard-content { 2 | margin-top: 15px; 3 | } 4 | 5 | #clinician-dashboard-content .fitted-panel { 6 | overflow-y: auto; 7 | overflow-x: hidden; 8 | text-align: left; 9 | /* 92px is the height of the on large screens Patient Control Panel, 20px is the height of the top margin of post encounter view content and a little extra for potential scroll bars*/ 10 | max-height: calc(100vh - 92px - 20px); 11 | min-height: 400px !important; 12 | } 13 | 14 | #clinician-dashboard-content .right-border-box { 15 | border-right: 1px dashed #ddd; 16 | padding-right: 1px; 17 | } 18 | 19 | #clinician-dashboard-content .full-panel { 20 | height: auto; 21 | } 22 | 23 | @media only screen and (max-width: 1200px) { 24 | #clinician-dashboard-content .fitted-panel { 25 | /* 117px is the height of the on large screens Patient Control Panel, 20px is the height of the top margin of post encounter view content and a little extra for potential scroll bars*/ 26 | max-height: calc(100vh - 117px - 20px); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/dashboard/CompassAppDashboard.scss: -------------------------------------------------------------------------------- 1 | #compass-app-dashboard-content { 2 | margin-top: 15px; 3 | 4 | .fitted-panel { 5 | overflow-y: auto; 6 | overflow-x: hidden; 7 | text-align: left; 8 | /* 106px is the height of the on large screens Patient Control Panel, 20px is the height of the top margin of post encounter view content and a little extra for potential scroll bars*/ 9 | max-height: calc(100vh - 92px - 20px); 10 | min-height: 400px !important; 11 | } 12 | 13 | .right-border-box { 14 | border-right: 2px dashed #ddd; 15 | } 16 | 17 | .full-panel { 18 | height: auto; 19 | } 20 | } 21 | 22 | @media only screen and (max-width: 1200px) { 23 | #compass-app-dashboard-content .fitted-panel { 24 | /* 117px is the height of the on large screens Patient Control Panel, 20px is the height of the top margin of post encounter view content and a little extra for potential scroll bars*/ 25 | max-height: calc(100vh - 117px - 20px); 26 | } 27 | } -------------------------------------------------------------------------------- /src/dashboard/NextPatientButton.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .patient-div { 4 | position: absolute; 5 | bottom: -1px; 6 | left: 0; 7 | width: 120px; 8 | height: 100px; 9 | background-color: #32556C; 10 | border-style: solid; 11 | border-color: #E1E1E1; 12 | border-left: none; 13 | border-bottom: none; 14 | border-radius: 0 50px 0 0; 15 | display: -webkit-flex; 16 | display: flex; 17 | -webkit-align-items: center; 18 | align-items: center; 19 | -webkit-justify-content: center; 20 | justify-content: center; 21 | text-align: center; 22 | z-index: 5 !important; 23 | &:hover { 24 | cursor: pointer; 25 | } 26 | } 27 | .inside { 28 | display: flex; 29 | align-items: center; 30 | justify-content: center; 31 | flex-grow: 1; 32 | height: 100%; 33 | border-radius: 0 50px 0 0; 34 | &:hover { 35 | background-color: rgba(0, 0, 0, 0.12); 36 | } 37 | } 38 | .patient-icon { 39 | color: white; 40 | display: inline-block; 41 | } -------------------------------------------------------------------------------- /src/dataaccess/GenericSmartOnFhirDstu2DataSource.js: -------------------------------------------------------------------------------- 1 | import McodeV09SmartOnFhirDataSource from "./McodeV09SmartOnFhirDataSource"; 2 | import processFHIRResources from './utils/fhir-entry-processor'; 3 | import mappers from './mappers'; 4 | 5 | class GenericSmartOnFhirDstu2DataSource extends McodeV09SmartOnFhirDataSource { 6 | constructor(props) { 7 | super(props); 8 | if (props && props.mapper) { 9 | const mapperClass = typeof props.mapper === 'string' ? mappers[props.mapper] : props.mapper; 10 | this.mapper = new mapperClass(props.mapperVariables); 11 | } 12 | } 13 | 14 | getPatient(id, callback) { 15 | super.fetchResources() 16 | .then(resources => callback(processFHIRResources(resources, this._client.patient.id, this.mapper))); 17 | } 18 | } 19 | 20 | export default GenericSmartOnFhirDstu2DataSource; -------------------------------------------------------------------------------- /src/dataaccess/IDataSource.js: -------------------------------------------------------------------------------- 1 | class IDataSource { 2 | getPatient(id, optionalCallback) { 3 | throw new Error("getPatient not implemented by " + this.constructor.name); 4 | } 5 | getListOfPatients() { 6 | throw new Error("getListOfPatients not implemented by " + this.constructor.name); 7 | } 8 | 9 | newPatient() { 10 | throw new Error("newPatient not implemented by " + this.constructor.name); 11 | } 12 | 13 | savePatient(patient) { 14 | throw new Error("savePatient not implemented by " + this.constructor.name); 15 | } 16 | 17 | // On the origins of this term see: https://en.wikipedia.org/wiki/Gestalt_(Mac_OS) 18 | getGestalt() { 19 | // 20 | // Shape of the gestalt : { 21 | // create: { 22 | // sync: bool, 23 | // async: bool 24 | // } 25 | // read: { 26 | // sync: bool, 27 | // async: bool 28 | // } 29 | // update: { 30 | // sync: bool, 31 | // async: bool 32 | // } 33 | // delete: { 34 | // sync: bool, 35 | // async: bool 36 | // } 37 | // } 38 | // 39 | throw new Error("getGestalt not implemented by " + this.constructor.name); 40 | } 41 | } 42 | 43 | export default IDataSource; -------------------------------------------------------------------------------- /src/dataaccess/NewPatientOnlyDataSource.js: -------------------------------------------------------------------------------- 1 | import IDataSource from './IDataSource'; 2 | import PatientRecord from '../patient/PatientRecord'; 3 | 4 | class NewPatientOnlyDataSource extends IDataSource { 5 | constructor() { 6 | super(); 7 | this._gestalt = { 8 | create: { 9 | async: false, 10 | sync: true 11 | }, 12 | read: { 13 | async: false, 14 | sync: false 15 | }, 16 | update: { 17 | async: false, 18 | sync: false 19 | }, 20 | delete: { 21 | async: false, 22 | sync: false 23 | } 24 | }; 25 | } 26 | getGestalt() { 27 | return this._gestalt; 28 | } 29 | getPatient(id) { 30 | console.error("loading of patients is not implemented in new patient only data source."); 31 | } 32 | getListOfPatients() { 33 | console.error("listing of patients is not implemented in new patient only data source."); 34 | } 35 | 36 | newPatient() { 37 | return new PatientRecord(null); 38 | } 39 | 40 | savePatient(patient) { 41 | console.error("saving of patients is not implemented in new patient only data source."); 42 | } 43 | } 44 | 45 | export default NewPatientOnlyDataSource; -------------------------------------------------------------------------------- /src/dataaccess/mappers/index.js: -------------------------------------------------------------------------------- 1 | import {mappers} from 'fhir-mapper'; 2 | import CernerSandboxMapper from './CernerSandboxMapper'; 3 | export default {...mappers, CernerSandboxMapper}; -------------------------------------------------------------------------------- /src/elements/Button.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from 'material-ui/styles'; 2 | import Button from 'material-ui/Button'; 3 | 4 | const styles = { 5 | root: { 6 | fontFamily: '"Open Sans", Arial, sans-serif', 7 | padding: '0px', 8 | margin: '2px' 9 | } 10 | }; 11 | 12 | export default withStyles(styles)(Button); 13 | -------------------------------------------------------------------------------- /src/elements/ChoiceButton.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from 'material-ui/styles'; 2 | import Button from './Button'; 3 | 4 | const styles = { 5 | root: { 6 | fontSize: '.8rem', 7 | height: "50px", 8 | margin: 0.5, 9 | width: "130px", 10 | backgroundColor: "white", 11 | textTransform: "none" 12 | } 13 | }; 14 | 15 | export default withStyles(styles)(Button); 16 | -------------------------------------------------------------------------------- /src/elements/MenuItem.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from 'material-ui/styles'; 2 | import MenuItem from 'material-ui/Menu/MenuItem'; 3 | import variable from '../styles/_variables.scss'; 4 | 5 | const styles = { 6 | root: { 7 | border: 0, 8 | fontFamily: '"Open Sans", Arial, sans-serif', 9 | fontSize: "0.8em", 10 | color: "#555", 11 | '&:hover': { 12 | backgroundColor: variable.state 13 | } 14 | } 15 | 16 | }; 17 | 18 | export default withStyles(styles)(MenuItem); 19 | -------------------------------------------------------------------------------- /src/elements/SearchBar.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import TextField from 'material-ui/TextField'; 4 | 5 | import './SearchBar.css'; 6 | 7 | export default class SearchBar extends Component { 8 | render () { 9 | return ( 10 |
11 | 12 | this.props.handleSearch(event.target.value)} 17 | /> 18 |
19 | ); 20 | } 21 | } 22 | 23 | SearchBar.propTypes = { 24 | searchString: PropTypes.string, 25 | handleSearch: PropTypes.func.isRequired, 26 | label: PropTypes.string.isRequired, 27 | }; 28 | -------------------------------------------------------------------------------- /src/elements/SearchBar.scss: -------------------------------------------------------------------------------- 1 | @import "../styles/variables"; 2 | 3 | #search-container { 4 | font-size: $size-m; 5 | color: $state; 6 | text-align: left; 7 | position: relative; 8 | display: inline-flex; 9 | align-items: flex-end; 10 | width: 100%; 11 | .search-icon { 12 | margin-right: 5px; 13 | padding-bottom: 10px; 14 | } 15 | .search-text { 16 | // This should be the width of the search-bars container, minus the size of the icon 17 | width: calc(100% - 25px); 18 | font-size: $size-m; 19 | color: $state; 20 | } 21 | } 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/elements/Select.js: -------------------------------------------------------------------------------- 1 | import { withStyles } from 'material-ui/styles'; 2 | import Select from 'material-ui/Select'; 3 | 4 | const styles = { 5 | root: { 6 | border: 0, 7 | fontFamily: '"Open Sans", Arial, sans-serif', 8 | fontSize: "0.9em", 9 | color: "#555", 10 | } 11 | 12 | }; 13 | 14 | export default withStyles(styles)(Select); 15 | -------------------------------------------------------------------------------- /src/forms/ClinicalTrialEnrollmentForm.css: -------------------------------------------------------------------------------- 1 | .tooltip-clinical-trial { 2 | position: relative; 3 | display: inline-block; 4 | } 5 | 6 | .tooltip-clinical-trial .tooltiptext-clinical-trial { 7 | visibility: hidden; 8 | width: 150px; 9 | background-color: #555; 10 | color: #fff; 11 | text-align: center; 12 | border-radius: 6px; 13 | padding: 10px 10px; 14 | position: absolute; 15 | z-index: 1; 16 | bottom: 125%; 17 | margin-left: 18px !important; 18 | opacity: 0; 19 | transition: opacity 1s; 20 | font-size: .8rem; 21 | } 22 | 23 | .tooltip-clinical-trial .large-clinical-trial { 24 | width: 220px; 25 | margin-left: -95px; 26 | } 27 | 28 | .tooltip-clinical-trial .tooltiptext-clinical-trial::after { 29 | content: ""; 30 | position: absolute; 31 | top: 100%; 32 | left: 50%; 33 | margin-left: -5px; 34 | border-width: 5px; 35 | border-style: solid; 36 | border-color: #555 transparent transparent transparent; 37 | } 38 | 39 | .tooltip-clinical-trial:hover .tooltiptext-clinical-trial { 40 | visibility: visible; 41 | opacity: 1; 42 | transition-delay: 0.5s; 43 | } 44 | 45 | .helper-text { 46 | color: #b1b1b1; 47 | } 48 | 49 | .MuiInput-inkbar-113:after{ 50 | background-color: #297DA2 !important; 51 | } 52 | 53 | .DayPickerInput-Overlay { 54 | z-index: 3; 55 | } -------------------------------------------------------------------------------- /src/forms/ClinicalTrialUnenrolledForm.css: -------------------------------------------------------------------------------- 1 | .tooltip-clinical-trial { 2 | position: relative; 3 | display: inline-block; 4 | } 5 | 6 | .tooltip-clinical-trial .tooltiptext-clinical-trial { 7 | visibility: hidden; 8 | width: 150px; 9 | background-color: #555; 10 | color: #fff; 11 | text-align: center; 12 | border-radius: 6px; 13 | padding: 10px 10px; 14 | position: absolute; 15 | z-index: 1; 16 | bottom: 125%; 17 | margin-left: 18px !important; 18 | opacity: 0; 19 | transition: opacity 1s; 20 | font-size: .8rem; 21 | } 22 | 23 | .tooltip-clinical-trial .large-clinical-trial { 24 | width: 220px; 25 | margin-left: -95px; 26 | } 27 | 28 | .tooltip-clinical-trial .tooltiptext-clinical-trial::after { 29 | content: ""; 30 | position: absolute; 31 | top: 100%; 32 | left: 50%; 33 | margin-left: -5px; 34 | border-width: 5px; 35 | border-style: solid; 36 | border-color: #555 transparent transparent transparent; 37 | } 38 | 39 | .tooltip-clinical-trial:hover .tooltiptext-clinical-trial { 40 | visibility: visible; 41 | opacity: 1; 42 | transition-delay: 0.5s; 43 | } 44 | 45 | .helper-text { 46 | color: #b1b1b1; 47 | } 48 | 49 | .MuiInput-inkbar-113:after{ 50 | background-color: #297DA2 !important; 51 | } 52 | 53 | .DayPickerInput-Overlay { 54 | z-index: 3; 55 | } -------------------------------------------------------------------------------- /src/forms/DataCaptureForm.css: -------------------------------------------------------------------------------- 1 | .btn_template { 2 | float: left; 3 | clear: left; 4 | margin-bottom: 20px; 5 | } 6 | -------------------------------------------------------------------------------- /src/forms/DatePicker.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/src/forms/DatePicker.css -------------------------------------------------------------------------------- /src/forms/DeceasedForm.css: -------------------------------------------------------------------------------- 1 | .helper-text { 2 | color: #b1b1b1; 3 | } 4 | 5 | .MuiInput-inkbar-113:after{ 6 | background-color: #297DA2 !important; 7 | } -------------------------------------------------------------------------------- /src/forms/FormList.css: -------------------------------------------------------------------------------- 1 | #list-panel { 2 | background-color: #F3F3F3; 3 | height: calc(100vh - 64px); 4 | text-align: left; 5 | overflow-y: auto; 6 | overflow: overlay; 7 | } 8 | 9 | #list-panel .list-element.unselected{ 10 | border-bottom: 1px solid #d9d9d9 !important; 11 | padding: 16px; 12 | line-height: 16px; 13 | } 14 | #list-panel .list-element.unselected.overview{ 15 | color: steelblue !important; 16 | padding: 16px; 17 | line-height: 16px; 18 | } 19 | 20 | #list-panel .list-element.selected{ 21 | background-color: white !important; 22 | font-weight: bold!important; 23 | -webkit-box-shadow: 0px 4px 5px -4px rgba(125,125,125,0.7); 24 | -moz-box-shadow: 0px 4px 5px -4px rgba(125,125,125,0.7); 25 | box-shadow: 0px 4px 5px -4px rgba(125,125,125,0.7); 26 | padding: 16px; 27 | line-height: 16px; 28 | } 29 | #list-panel .list-element.selected.overview{ 30 | color: steelblue !important; 31 | padding: 16px; 32 | line-height: 16px; 33 | } 34 | -------------------------------------------------------------------------------- /src/forms/FormSearch.css: -------------------------------------------------------------------------------- 1 | 2 | #form-search { 3 | padding: 15px 0; 4 | background-color: #fbfbfb; 5 | text-align: left; 6 | height: 50px; 7 | border-bottom: 1px solid #d9d9d9 !important; 8 | } -------------------------------------------------------------------------------- /src/forms/FormSearch.jsx: -------------------------------------------------------------------------------- 1 | // React imports 2 | import React, {Component} from 'react'; 3 | // material-ui 4 | import TextField from 'material-ui/TextField'; 5 | import FontIcon from 'material-ui/FontIcon'; 6 | // Styling 7 | import './FormSearch.css'; 8 | 9 | class FormSearch extends Component { 10 | constructor(props) { 11 | super(props); 12 | 13 | this.underlineStyle = { 14 | borderColor: "#17263f", 15 | }; 16 | } 17 | 18 | handleSearch(searchValue) { 19 | console.log(); 20 | } 21 | 22 | render() { 23 | return ( 24 | 38 | ); 39 | } 40 | } 41 | 42 | export default FormSearch; 43 | -------------------------------------------------------------------------------- /src/forms/LandingPageForm.css: -------------------------------------------------------------------------------- 1 | .landing-page-title { 2 | color: steelblue; 3 | } 4 | 5 | ol, ol li { 6 | margin-left: 0; 7 | padding-left: 0; 8 | } 9 | 10 | ol { 11 | margin-left: 1.3em; 12 | } 13 | 14 | .landing-page-about { 15 | margin-bottom: 40px; 16 | } 17 | 18 | .landing-page-using li { 19 | margin: 10px 0; 20 | } -------------------------------------------------------------------------------- /src/forms/MultiChoiceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ChoiceButton from '../elements/ChoiceButton'; 4 | 5 | import "./MultiChoiceButton.css"; 6 | 7 | class MultiChoiceButton extends Component { 8 | render() { 9 | let className = "multi-choice-button "; 10 | className += (this.props.className) ? `${this.props.className} ` : ""; 11 | className += (this.props.isSelected) ? "selected " : ""; 12 | 13 | return ( 14 | 21 | {this.props.buttonText} 22 | 23 | ); 24 | } 25 | } 26 | 27 | MultiChoiceButton.propTypes= { 28 | buttonKey: PropTypes.number.isRequired, 29 | buttonText: PropTypes.string.isRequired, 30 | onClick: PropTypes.func.isRequired, 31 | className: PropTypes.string, 32 | marginSize: PropTypes.string.isRequired, 33 | isDisabled: PropTypes.bool, 34 | }; 35 | 36 | export default MultiChoiceButton; 37 | -------------------------------------------------------------------------------- /src/forms/MultiChoiceButton.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .multi-choice-button { 4 | border-radius: 5px !important; 5 | border-color: $background; 6 | line-height: 0.9rem !important; 7 | margin: 2px !important; 8 | 9 | &.placeholder-button { 10 | width: 115px !important; 11 | height: 47px !important; 12 | border-radius: 4px !important; 13 | color: $body-black !important; 14 | box-shadow: 0px 0px 0px 0px #DDDDDD, 0px 0px 0px 0px #DDDDDD, 0px 0px 0px 1px #DDDDDD !important; 15 | } 16 | 17 | &.selected { 18 | background-color: $interface-blue !important; 19 | color: $background !important; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/forms/ProgressionForm.css: -------------------------------------------------------------------------------- 1 | .tooltip-progression-form { 2 | position: relative; 3 | display: inline-block; 4 | } 5 | 6 | .tooltip-progression-form .tooltiptext { 7 | visibility: hidden; 8 | width: 150px; 9 | background-color: #555; 10 | color: #fff; 11 | text-align: center; 12 | border-radius: 5px; 13 | padding: 10px 10px; 14 | position: absolute; 15 | z-index: 1; 16 | bottom: 125%; 17 | margin-left: 18px !important; 18 | opacity: 0; 19 | transition: opacity 1s; 20 | font-size: .8rem; 21 | } 22 | 23 | .tooltip-progression-form .large { 24 | width: 220px; 25 | margin-left: -95px; 26 | } 27 | 28 | .tooltip-progression-form .tooltiptext::after { 29 | content: ""; 30 | position: absolute; 31 | top: 100%; 32 | left: 50%; 33 | margin-left: -5px; 34 | border-width: 5px; 35 | border-style: solid; 36 | border-color: #555 transparent transparent transparent; 37 | } 38 | 39 | .tooltip-progression-form:hover .tooltiptext { 40 | visibility: visible; 41 | opacity: 1; 42 | transition-delay: 0.5s; 43 | } 44 | 45 | .helper-text { 46 | color: #b1b1b1; 47 | } 48 | 49 | .MuiInput-inkbar-113:after{ 50 | background-color: #297DA2 !important; 51 | } 52 | 53 | .btn-group-reason-progression { 54 | margin: 0 10px; 55 | } 56 | 57 | h4 span.helper-text { 58 | font-size: .9em; 59 | font-style: italic; 60 | } -------------------------------------------------------------------------------- /src/forms/SingleChoiceButton.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import ChoiceButton from '../elements/ChoiceButton'; 4 | 5 | import "./SingleChoiceButton.css"; 6 | 7 | class SingleChoiceButton extends Component { 8 | render() { 9 | let className = "single-choice-button "; 10 | className += (this.props.className) ? `${this.props.className} ` : ""; 11 | className += (this.props.isSelected) ? "selected " : ""; 12 | 13 | return ( 14 | 25 | {this.props.buttonText} 26 | 27 | ); 28 | } 29 | } 30 | 31 | SingleChoiceButton.propTypes= { 32 | buttonKey: PropTypes.number.isRequired, 33 | buttonText: PropTypes.string.isRequired, 34 | onClick: PropTypes.func.isRequired, 35 | className: PropTypes.string, 36 | marginSize: PropTypes.string, 37 | isDisabled: PropTypes.bool, 38 | }; 39 | 40 | export default SingleChoiceButton; 41 | -------------------------------------------------------------------------------- /src/forms/SingleChoiceButton.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .single-choice-button { 4 | border-radius: 5px !important; 5 | border: 2px solid; 6 | border-color: $background; 7 | line-height: 0.9rem !important; 8 | margin: .5px !important; 9 | 10 | &.placeholder-button { 11 | width: 115px !important; 12 | height: 47px !important; 13 | border-radius: 0px !important; 14 | color: $body-black !important; 15 | box-shadow: 0px 0px 0px 0px #DDDDDD, 0px 0px 0px 0px #DDDDDD, 0px 0px 0px 1px #DDDDDD !important; 16 | } 17 | 18 | &.selected { 19 | background-color: $interface-blue !important; 20 | color: $background !important; 21 | } 22 | } -------------------------------------------------------------------------------- /src/forms/index.js: -------------------------------------------------------------------------------- 1 | export {default as StagingForm} from './StagingForm'; 2 | export {default as ProgressionForm} from './ProgressionForm'; 3 | export {default as ToxicityForm} from './ToxicityForm'; 4 | export {default as DeceasedForm} from './DeceasedForm'; 5 | export {default as ClinicalTrialEnrollmentForm} from './ClinicalTrialEnrollmentForm'; 6 | export {default as ClinicalTrialUnenrolledForm} from './ClinicalTrialUnenrolledForm'; -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | height: 100%; 5 | font-family: "Open Sans", Arial, sans-serif; 6 | color: #232323; 7 | } 8 | 9 | html { 10 | font-size: 16px; 11 | } 12 | 13 | ::-webkit-scrollbar { 14 | width: 10px; 15 | height: 12px; 16 | } 17 | 18 | ::-webkit-scrollbar-track { 19 | /*-webkit-box-shadow: inset 0 0 1px rgba(0,0,0,0); 20 | */ 21 | background-color: #F0F0F0; 22 | border-radius: 5px; 23 | } 24 | 25 | ::-webkit-scrollbar-thumb { 26 | border-radius: 10px; 27 | background-color: #F0F0F0; 28 | /* -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.5); 29 | */ 30 | } 31 | /* Hide scrollbar for IE until a specific height or width is reached (This is for the case in full app, when a note is open)*/ 32 | /* Commenting out this line for now because it causes hthe landing page to not be scrollable in IE*/ 33 | /* @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { 34 | body { 35 | overflow: hidden; 36 | } 37 | } */ 38 | 39 | /* This line is needed so that users are able to scroll in IE on the landing page */ 40 | @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) and (max-height: 525px), (max-width: 1160px) { 41 | body { 42 | overflow: auto; } } 43 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import 'babel-polyfill'; 2 | import 'es6-shim'; 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import { Provider } from 'react-redux'; 6 | import { ConnectedRouter } from 'react-router-redux'; 7 | import injectTapEventPlugin from 'react-tap-event-plugin'; 8 | 9 | import store, { history } from './store/configureStore'; 10 | import App from './containers/App'; 11 | import './index.css'; 12 | 13 | // Needed for onTouchTap 14 | // http://stackoverflow.com/a/34015469/988941 15 | injectTapEventPlugin(); 16 | 17 | const target = document.querySelector('#root'); 18 | const base = (global.CONFIG && global.CONFIG.basename) ? global.CONFIG.basename : '/'; 19 | render( 20 | 21 | 22 |
23 | 24 |
25 |
26 |
, 27 | target 28 | ); 29 | -------------------------------------------------------------------------------- /src/lib/FHIRMapper.js: -------------------------------------------------------------------------------- 1 | import Lang from 'lodash'; 2 | 3 | // map FHIR resourceType to SHR entryType 4 | const mapOptions = { 5 | "Patient": "http://standardhealthrecord.org/demographics/PersonOfRecord", 6 | "Condition": "http://standardhealthrecord.org/condition/Condition" 7 | } 8 | 9 | 10 | function mapToEntryTypes(entry){ 11 | // TODO: Support returning multiple entry types from a single FHIR entry 12 | const entryType = mapOptions[entry.resource.resourceType]; 13 | return Lang.isUndefined(entryType) ? [null] : [entryType]; 14 | }; 15 | 16 | export { 17 | mapToEntryTypes 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/cancer_lookup.jsx: -------------------------------------------------------------------------------- 1 | import * as codeableConceptUtils from '../model/CodeableConceptUtils'; 2 | 3 | const cancerOptions = [ 4 | { 5 | name: 'Invasive ductal carcinoma of breast', 6 | description: "Invasive ductal carcinoma of breast", 7 | code: "408643008", 8 | codeSystem: "http://snomed.info/sct" 9 | }, 10 | { 11 | name: 'Gastrointestinal stromal tumor', 12 | description: "Gastrointestinal stromal tumor", 13 | code: "420120006", 14 | codeSystem: "http://snomed.info/sct" 15 | }]; 16 | 17 | /* 18 | * Searches for value in cancerOptions list 19 | * Will return CodeableConcept object with empty strings if not found 20 | * If value found in list, function will return CodeableConcept with value, codeSystem, and displayText 21 | */ 22 | export function getCancerCodeableConcept(possibleSpecificType) { 23 | return codeableConceptUtils.getCodeableConceptFromOptions(possibleSpecificType, cancerOptions); 24 | } 25 | -------------------------------------------------------------------------------- /src/lib/cql-execution/CQLExecutionEngine.js: -------------------------------------------------------------------------------- 1 | import cql from 'cql-execution'; 2 | import cqlfhir from 'cql-exec-fhir'; 3 | import CodeService from './codeservice/CodeService'; 4 | import valueset_db from './codeservice/vsac_cache/valueset-db.json'; 5 | 6 | 7 | /* 8 | * returns CQL execution results when passed in a CQL file(JSON ELM) and set of patients 9 | * cqlLogic is a JSON ELM file, which was converted from a CQL file defining logic 10 | * psource is an array of patients each represented as a FHIR bundle 11 | */ 12 | export function getCQLResults(cqlLogic, psource){ 13 | // Set up library 14 | const lib = new cql.Library(cqlLogic); 15 | 16 | // Create Patient Source 17 | const patientSource = cqlfhir.PatientSource.FHIRv102(); 18 | 19 | // Load patientsource with patients 20 | patientSource.loadBundles(psource); 21 | 22 | // Set up code service and load valuesets 23 | const codeService = new CodeService(valueset_db); 24 | 25 | const executor = new cql.Executor(lib, codeService); 26 | return executor.exec(patientSource); 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/cql-execution/codeservice/README.md: -------------------------------------------------------------------------------- 1 | valueSetDownloader will download all value sets specified in provided JSON ELM(generated from CQL) file and save it in `vsac_cache/valueset-db.json` to be used by CQLExecutionEngine. 2 | ``` 3 | node valueSetDownloader.js [vsacUserName] [vsacPassword] 4 | ``` 5 | 6 | Downloading value set definitions from VSAC requires a valid UMLS account. Alternately, the UMLS username and password can be provided via UMLS_USER_NAME and UMLS_PASSWORD environment variables. 7 | 8 | -------------------------------------------------------------------------------- /src/lib/cql-execution/codeservice/valueSetDownloader.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const cql = require('cql-execution'); 4 | const cqlfhir = require('cql-exec-fhir'); 5 | const cqlvsac = require('cql-exec-vsac'); 6 | 7 | if (process.argv.length < 3) { 8 | console.error('JSON ELM file not provided.'); 9 | process.exit(1); 10 | } 11 | 12 | let vsacUser, vsacPass; 13 | if (process.argv.length===5) { 14 | [vsacUser, vsacPass] = process.argv.slice(3); 15 | } 16 | 17 | // Set up the library 18 | const elmFile = JSON.parse(fs.readFileSync(process.argv[2], 'utf8')); 19 | 20 | const library = new cql.Library(elmFile); 21 | 22 | // Extract the value sets from the ELM 23 | let valueSets = []; 24 | if (elmFile.library && elmFile.library.valueSets && elmFile.library.valueSets.def) { 25 | valueSets = elmFile.library.valueSets.def; 26 | } 27 | 28 | // Set up the code service, loading from the cache if it exists 29 | const codeService = new cqlvsac.CodeService(path.join(__dirname, 'vsac_cache'), true); 30 | // Ensure value sets, downloading any missing value sets 31 | codeService.ensureValueSets(valueSets, vsacUser, vsacPass) 32 | .then(() => { 33 | // Value sets are loaded 34 | console.log('Value sets downloaded!'); 35 | }) 36 | .catch((err) => { 37 | // There was an error downloading the value sets! 38 | console.error('Error downloading value sets', err); 39 | }); -------------------------------------------------------------------------------- /src/lib/cql-execution/example/cql/BreastCancer.cql: -------------------------------------------------------------------------------- 1 | // NOTE: This is a simplified example, designed only for the purpose of demonstrating how to 2 | // use the cql-execution, cql-exec-fhir, and cql-exec-vsac javascript modules. This CQL 3 | // is NOT clinically validated and should NOT be used in a clinical setting. 4 | 5 | library BreastCancer version '1.0.0' 6 | using FHIR version '1.0.2' 7 | include FHIRHelpers version '1.0.2' called FHIRHelpers 8 | 9 | // Value set and codes loosely borrowed from CMS 123v7 10 | codesystem "SNOMEDCT": 'http://snomed.info/sct' 11 | valueset "Breast Cancer": '2.16.840.1.113883.3.526.2.98' 12 | 13 | context Patient 14 | 15 | define InDemographic: 16 | AgeInYears() between 18 and 75 17 | 18 | define HasBreastCancer: 19 | exists( 20 | [Condition: "Breast Cancer"] C 21 | where C.verificationStatus.value = 'confirmed' 22 | ) 23 | 24 | define MeetsInclusionCriteria: 25 | InDemographic 26 | and HasBreastCancer 27 | -------------------------------------------------------------------------------- /src/lib/cql-execution/example/cql/age.cql: -------------------------------------------------------------------------------- 1 | library AgeAtMP version '1' 2 | 3 | using QUICK 4 | 5 | parameter MeasurementPeriod default Interval[DateTime(2013, 1, 1, 0, 0, 0, 0), DateTime(2014, 1, 1, 0, 0, 0, 0)) 6 | 7 | context Patient 8 | 9 | define InDemographic: 10 | AgeInYearsAt(start of MeasurementPeriod) >= 2 and AgeInYearsAt(start of MeasurementPeriod) < 18 -------------------------------------------------------------------------------- /src/lib/cql-execution/example/cql/gender.cql: -------------------------------------------------------------------------------- 1 | library gender version '1' 2 | 3 | using FHIR version '1.0.2' 4 | 5 | include FHIRHelpers version '1.0.2' called FHIRHelpers 6 | 7 | context Patient 8 | 9 | define "Is Female": 10 | Patient.gender.value = 'female' 11 | -------------------------------------------------------------------------------- /src/lib/cql-execution/example/patients/No_Foot_Exam.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Bundle", 3 | "id": "No_Foot_Exam_Bundle", 4 | "type": "collection", 5 | "entry": [ 6 | { 7 | "resource": { 8 | "resourceType": "Patient", 9 | "id": "No_Foot_Exam", 10 | "gender": "female", 11 | "birthDate": "1973-04-28" 12 | } 13 | }, 14 | { 15 | "resource": { 16 | "resourceType": "Condition", 17 | "id": "1-1", 18 | "clinicalStatus": "active", 19 | "verificationStatus": "confirmed", 20 | "code": { 21 | "coding": [ 22 | { 23 | "system": "http://snomed.info/sct", 24 | "code": "44054006", 25 | "display": "Diabetes mellitus type 2 (disorder)" 26 | } 27 | ] 28 | }, 29 | "subject": { 30 | "reference": "Patient/No_Foot_Exam" 31 | }, 32 | "onsetDateTime": "2012-10-05" 33 | } 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /src/lib/react-minimap/index.js: -------------------------------------------------------------------------------- 1 | export default Minimap from './react-minimap' 2 | export {default as Child} from './components/Child' -------------------------------------------------------------------------------- /src/lib/react-minimap/react-minimap.scss: -------------------------------------------------------------------------------- 1 | @import "../../styles/variables"; 2 | 3 | .minimap-viewport { 4 | position: absolute; 5 | box-sizing: border-box; 6 | background-color: rgba(0, 0, 0, 0.4); 7 | z-index: 1; 8 | cursor: move; 9 | -webkit-transition: width .5s, height .5s; /* Safari */ 10 | transition: width .5s, height .5s; 11 | } 12 | 13 | .minimap { 14 | float: right; 15 | position: fixed; 16 | right: 0; 17 | z-index: 1; 18 | margin: 10px; 19 | background-color: rgba(0, 0, 0, 0.29); 20 | border: 1px solid rgba(0, 0, 0, 0.17); 21 | } 22 | 23 | .minimap-container { 24 | overflow: scroll; 25 | width: 100%; 26 | height: 100%; 27 | } 28 | 29 | .minimap-children-wrapper { 30 | position: absolute; 31 | left: 0; 32 | } 33 | 34 | .minimap-children { 35 | background: #CCC; 36 | border: 1px solid black; 37 | box-sizing: border-box; 38 | } 39 | 40 | .edit-button { 41 | border: 1px solid $state !important; 42 | color: $interface-blue-text; 43 | margin-left: 4px; 44 | } 45 | 46 | .edit_visible { 47 | border: 1px solid rgba(2, 141, 234, .4) !important; 48 | color: $body-black !important; 49 | } 50 | 51 | .edit_invisible { 52 | border: none !important; 53 | background-color: $contrast-gray !important; 54 | color: $state !important; 55 | } 56 | -------------------------------------------------------------------------------- /src/lib/slate-suggestions-dist/caret-position.js: -------------------------------------------------------------------------------- 1 | // Acquire from http://jsfiddle.net/gliheng/vbucs/12/ 2 | function position($node, offsetx, offsety) { 3 | offsetx = offsetx || 0; 4 | offsety = offsety || 0; 5 | 6 | let nodeLeft = 0; 7 | let nodeTop = 0; 8 | if ($node) { 9 | nodeLeft = $node.offsetLeft; 10 | nodeTop = $node.offsetTop; 11 | } 12 | 13 | const pos = { left: 0, top: 0 }; 14 | 15 | if (document.selection) { 16 | const range = document.selection.createRange(); 17 | pos.left = range.offsetLeft + offsetx - nodeLeft; 18 | pos.top = range.offsetTop + offsety - nodeTop; 19 | } else if (window.getSelection) { 20 | const native = window.getSelection(); 21 | const range = native.getRangeAt(0); 22 | const rect = range.getBoundingClientRect(); 23 | pos.top = rect.top 24 | + offsety 25 | + nodeTop; 26 | 27 | pos.left = rect.left + 28 | offsetx + 29 | nodeLeft + 30 | rect.width / 2; 31 | } 32 | return pos; 33 | }; 34 | 35 | export default position 36 | -------------------------------------------------------------------------------- /src/lib/slate-suggestions-dist/constants.js: -------------------------------------------------------------------------------- 1 | export const UP_ARROW_KEY = 38 2 | export const DOWN_ARROW_KEY = 40 3 | export const ENTER_KEY = 13 4 | export const RESULT_SIZE = 5 5 | -------------------------------------------------------------------------------- /src/lib/slate-suggestions-dist/current-word.js: -------------------------------------------------------------------------------- 1 | function getCurrentWord(text, index, trigger) { 2 | const startIndex = text.lastIndexOf(trigger, index + 1) 3 | return text.substring(startIndex, index + 1) 4 | } 5 | 6 | export default getCurrentWord 7 | -------------------------------------------------------------------------------- /src/lib/slate-suggestions-dist/suggestion-item.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | class SuggestionItem extends React.Component { 4 | 5 | onClick = (e) => { 6 | this.props.closePortal() 7 | 8 | const { editor, suggestion, appendSuggestion } = this.props 9 | 10 | const state = appendSuggestion(suggestion) 11 | 12 | editor.onChange(state) 13 | } 14 | 15 | onMouseEnter = () => { 16 | this.props.setSelectedIndex(this.props.index) 17 | } 18 | 19 | render = () => { 20 | return ( 21 |
  • 26 | {this.props.suggestion.suggestion} 27 |
  • 28 | ); 29 | } 30 | } 31 | 32 | export default SuggestionItem 33 | -------------------------------------------------------------------------------- /src/lib/slate/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains the core logic of Slate. It's separated further into a series of directories: 3 | 4 | - [**Components**](./components) — containing the React components Slate renders. 5 | - [**Constants**](./constants) — containing constants that are used in Slate's codebase. 6 | - [**Models**](./models) — containing the models that define Slate's internal data structure. 7 | - [**Plugins**](./plugins) — containing the plugins that ship with Slate by default. 8 | - [**Schemas**](./schemas) - containing the schemas that ship with Slate by default. 9 | - [**Serializers**](./serializers) — containing the serializers that ship with Slate by default. 10 | - [**Transforms**](./transforms) — containing the transforms that are used to alter a Slate document. 11 | - [**Utils**](./utils) — containing a few private convenience modules. 12 | 13 | Feel free to poke around in each of them to learn more! 14 | -------------------------------------------------------------------------------- /src/lib/slate/constants/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains constants that are referenced elsewhere in Slate's codebase. They are kept here for consistency. 3 | -------------------------------------------------------------------------------- /src/lib/slate/constants/is-dev.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Is in development? 4 | * 5 | * @type {Boolean} 6 | */ 7 | 8 | const IS_DEV = ( 9 | typeof process !== 'undefined' && 10 | process.env && 11 | process.env.NODE_ENV !== 'production' 12 | ) 13 | 14 | /** 15 | * Export. 16 | * 17 | * @type {Boolean} 18 | */ 19 | 20 | export default IS_DEV 21 | -------------------------------------------------------------------------------- /src/lib/slate/constants/types.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Slate-specific data transfer types. 4 | * 5 | * @type {Object} 6 | */ 7 | 8 | const TYPES = { 9 | FRAGMENT: 'application/x-slate-fragment', 10 | NODE: 'application/x-slate-node', 11 | } 12 | 13 | /** 14 | * Export. 15 | * 16 | * @type {Object} 17 | */ 18 | 19 | export default TYPES 20 | -------------------------------------------------------------------------------- /src/lib/slate/models/data.js: -------------------------------------------------------------------------------- 1 | 2 | import { Map } from 'immutable' 3 | 4 | /** 5 | * Data. 6 | * 7 | * This isn't an immutable record, it's just a thin wrapper around `Map` so that 8 | * we can allow for more convenient creation. 9 | * 10 | * @type {Object} 11 | */ 12 | 13 | const Data = { 14 | 15 | /** 16 | * Create a new `Data` with `properties`. 17 | * 18 | * @param {Object} properties 19 | * @return {Data} data 20 | */ 21 | 22 | create(properties = {}) { 23 | return Map.isMap(properties) 24 | ? properties 25 | : new Map(properties) 26 | } 27 | 28 | } 29 | 30 | /** 31 | * Export. 32 | * 33 | * @type {Object} 34 | */ 35 | 36 | export default Data 37 | -------------------------------------------------------------------------------- /src/lib/slate/models/range.js: -------------------------------------------------------------------------------- 1 | 2 | import Character from './character' 3 | import Mark from './mark' 4 | import { Record, Set } from 'immutable' 5 | 6 | /** 7 | * Default properties. 8 | * 9 | * @type {Object} 10 | */ 11 | 12 | const DEFAULTS = { 13 | marks: new Set(), 14 | text: '', 15 | } 16 | 17 | /** 18 | * Range. 19 | * 20 | * @type {Range} 21 | */ 22 | 23 | class Range extends new Record(DEFAULTS) { 24 | 25 | /** 26 | * Create a new `Range` with `properties`. 27 | * 28 | * @param {Object|Range} properties 29 | * @return {Range} 30 | */ 31 | 32 | static create(properties = {}) { 33 | if (properties instanceof Range) return properties 34 | properties.text = properties.text 35 | properties.marks = Mark.createSet(properties.marks) 36 | return new Range(properties) 37 | } 38 | 39 | /** 40 | * Get the node's kind. 41 | * 42 | * @return {String} 43 | */ 44 | 45 | get kind() { 46 | return 'range' 47 | } 48 | 49 | /** 50 | * Return range as a list of characters 51 | * 52 | * @return {List} 53 | */ 54 | 55 | getCharacters() { 56 | const { marks } = this 57 | 58 | return Character.createList(this.text 59 | .split('') 60 | .map((char) => { 61 | return Character.create({ 62 | text: char, 63 | marks 64 | }) 65 | })) 66 | } 67 | 68 | } 69 | 70 | /** 71 | * Export. 72 | * 73 | * @type {Range} 74 | */ 75 | 76 | export default Range 77 | -------------------------------------------------------------------------------- /src/lib/slate/plugins/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains the only plugin that ships with Slate by default, which controls all of the "core" logic. For example, it handles splitting apart paragraphs when `enter` is pressed, or inserting plain text content from the clipboard on paste. 3 | -------------------------------------------------------------------------------- /src/lib/slate/schemas/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains the core schema that ships with Slate by default, which controls all of the "core" document and selection validation logic. For example, it ensures that two adjacent text nodes are always joined, or that the top-level document only ever contains block nodes. It is not exposed by default, since it is only needed internally. 3 | -------------------------------------------------------------------------------- /src/lib/slate/serializers/plain.js: -------------------------------------------------------------------------------- 1 | 2 | import Raw from '../serializers/raw' 3 | 4 | /** 5 | * Deserialize a plain text `string` to a state. 6 | * 7 | * @param {String} string 8 | * @param {Object} options 9 | * @property {Boolean} toRaw 10 | * @return {State} 11 | */ 12 | 13 | function deserialize(string, options = {}) { 14 | const raw = { 15 | kind: 'state', 16 | document: { 17 | kind: 'document', 18 | nodes: string.split('\n').map((line) => { 19 | return { 20 | kind: 'block', 21 | type: 'line', 22 | nodes: [ 23 | { 24 | kind: 'text', 25 | ranges: [ 26 | { 27 | text: line, 28 | marks: [], 29 | } 30 | ] 31 | } 32 | ] 33 | } 34 | }), 35 | } 36 | } 37 | 38 | return options.toRaw ? raw : Raw.deserialize(raw) 39 | } 40 | 41 | /** 42 | * Serialize a `state` to plain text. 43 | * 44 | * @param {State} state 45 | * @return {String} 46 | */ 47 | 48 | function serialize(state) { 49 | return state.document.nodes 50 | .map(block => block.text) 51 | .join('\n') 52 | } 53 | 54 | /** 55 | * Export. 56 | * 57 | * @type {Object} 58 | */ 59 | 60 | export default { 61 | deserialize, 62 | serialize 63 | } 64 | -------------------------------------------------------------------------------- /src/lib/slate/transforms/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains all of the transforms that ship with Slate by default. For example, transforms like `insertText` or `addMarkAtRange`. 3 | -------------------------------------------------------------------------------- /src/lib/slate/transforms/call.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Transforms. 4 | * 5 | * @type {Object} 6 | */ 7 | 8 | const Transforms = {} 9 | 10 | /** 11 | * Call a `fn` as if it was a core transform. This is a convenience method to 12 | * make using non-core transforms easier to read and chain. 13 | * 14 | * @param {Transform} transform 15 | * @param {Function} fn 16 | * @param {Mixed} ...args 17 | */ 18 | 19 | Transforms.call = (transform, fn, ...args) => { 20 | fn(transform, ...args) 21 | return 22 | } 23 | 24 | /** 25 | * Export. 26 | * 27 | * @type {Object} 28 | */ 29 | 30 | export default Transforms 31 | -------------------------------------------------------------------------------- /src/lib/slate/transforms/index.js: -------------------------------------------------------------------------------- 1 | 2 | import ApplyOperation from './apply-operation' 3 | import AtCurrentRange from './at-current-range' 4 | import AtRange from './at-range' 5 | import ByKey from './by-key' 6 | import Call from './call' 7 | import Normalize from './normalize' 8 | import OnHistory from './on-history' 9 | import OnSelection from './on-selection' 10 | import Operations from './operations' 11 | 12 | /** 13 | * Export. 14 | * 15 | * @type {Object} 16 | */ 17 | 18 | export default { 19 | ...ApplyOperation, 20 | ...AtCurrentRange, 21 | ...AtRange, 22 | ...ByKey, 23 | ...Call, 24 | ...Normalize, 25 | ...OnHistory, 26 | ...OnSelection, 27 | ...Operations, 28 | } 29 | -------------------------------------------------------------------------------- /src/lib/slate/utils/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This directory contains a series of utilities that Slate uses internally. They're pulled out here to re-use code and to enforce consistency. You'll have to read the source to see what they do, but it's all commented so don't worry! 3 | -------------------------------------------------------------------------------- /src/lib/slate/utils/extend-selection.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Extends the given selection to a given node and offset 4 | * 5 | * @param {Selection} selection Selection instance 6 | * @param {Element} el Node to extend to 7 | * @param {Number} offset Text offset to extend to 8 | * @returns {Selection} Mutated Selection instance 9 | */ 10 | 11 | function extendSelection(selection, el, offset) { 12 | // Use native method when possible 13 | if (typeof selection.extend === 'function') return selection.extend(el, offset) 14 | 15 | // See https://gist.github.com/tyler-johnson/0a3e8818de3f115b2a2dc47468ac0099 16 | const range = document.createRange() 17 | const anchor = document.createRange() 18 | anchor.setStart(selection.anchorNode, selection.anchorOffset) 19 | 20 | const focus = document.createRange() 21 | focus.setStart(el, offset) 22 | 23 | const v = focus.compareBoundaryPoints(Range.START_TO_START, anchor) 24 | if (v >= 0) { // Focus is after anchor 25 | range.setStart(selection.anchorNode, selection.anchorOffset) 26 | range.setEnd(el, offset) 27 | } else { // Anchor is after focus 28 | range.setStart(el, offset) 29 | range.setEnd(selection.anchorNode, selection.anchorOffset) 30 | } 31 | 32 | selection.removeAllRanges() 33 | selection.addRange(range) 34 | 35 | return selection 36 | } 37 | 38 | 39 | /** 40 | * Export. 41 | * 42 | * @type {Function} 43 | */ 44 | 45 | export default extendSelection 46 | -------------------------------------------------------------------------------- /src/lib/slate/utils/find-closest-node.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Find the closest ancestor of a DOM `element` that matches a given selector. 4 | * 5 | * @param {Element} node 6 | * @param {String} selector 7 | * @return {Element} 8 | */ 9 | 10 | function findClosestNode(node, selector) { 11 | if (typeof node.closest === 'function') return node.closest(selector) 12 | 13 | // See https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill 14 | const matches = (node.document || node.ownerDocument).querySelectorAll(selector) 15 | let i 16 | let parentNode = node 17 | do { 18 | i = matches.length 19 | while (--i >= 0 && matches.item(i) !== parentNode); 20 | } 21 | while ((i < 0) && (parentNode = parentNode.parentElement)) 22 | 23 | return parentNode 24 | } 25 | 26 | /** 27 | * Export. 28 | * 29 | * @type {Function} 30 | */ 31 | 32 | export default findClosestNode 33 | -------------------------------------------------------------------------------- /src/lib/slate/utils/find-deepest-node.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Find the deepest descendant of a DOM `element`. 4 | * 5 | * @param {Element} node 6 | * @return {Element} 7 | */ 8 | 9 | function findDeepestNode(element) { 10 | return element.firstChild 11 | ? findDeepestNode(element.firstChild) 12 | : element 13 | } 14 | 15 | /** 16 | * Export. 17 | * 18 | * @type {Function} 19 | */ 20 | 21 | export default findDeepestNode 22 | -------------------------------------------------------------------------------- /src/lib/slate/utils/find-dom-node.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Find the DOM node for a `node`. 4 | * 5 | * @param {Node} node 6 | * @return {Element} 7 | */ 8 | 9 | function findDOMNode(node) { 10 | const el = window.document.querySelector(`[data-key="${node.key}"]`) 11 | 12 | if (!el) { 13 | throw new Error(`Unable to find a DOM node for "${node.key}". This is 14 | often because of forgetting to add \`props.attributes\` to a component 15 | returned from \`renderNode\`.`) 16 | } 17 | 18 | return el 19 | } 20 | 21 | /** 22 | * Export. 23 | * 24 | * @type {Function} 25 | */ 26 | 27 | export default findDOMNode 28 | -------------------------------------------------------------------------------- /src/lib/slate/utils/generate-key.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * An auto-incrementing index for generating keys. 4 | * 5 | * @type {Number} 6 | */ 7 | 8 | let n 9 | 10 | /** 11 | * The global key generating function. 12 | * 13 | * @type {Function} 14 | */ 15 | 16 | let generate 17 | 18 | /** 19 | * Generate a key. 20 | * 21 | * @return {String} 22 | */ 23 | 24 | function generateKey() { 25 | return generate() 26 | } 27 | 28 | /** 29 | * Set a different unique ID generating `function`. 30 | * 31 | * @param {Function} func 32 | */ 33 | 34 | function setKeyGenerator(func) { 35 | generate = func 36 | } 37 | 38 | /** 39 | * Reset the key generating function to its initial state. 40 | */ 41 | 42 | function resetKeyGenerator() { 43 | n = 0 44 | generate = () => `${n++}` 45 | } 46 | 47 | /** 48 | * Set the initial state. 49 | */ 50 | 51 | resetKeyGenerator() 52 | 53 | /** 54 | * Export. 55 | * 56 | * @type {Object} 57 | */ 58 | 59 | export { 60 | generateKey as default, 61 | setKeyGenerator, 62 | resetKeyGenerator 63 | } 64 | -------------------------------------------------------------------------------- /src/lib/slate/utils/get-point.js: -------------------------------------------------------------------------------- 1 | 2 | import OffsetKey from './offset-key' 3 | 4 | /** 5 | * Get a point from a native selection's DOM `element` and `offset`. 6 | * 7 | * @param {Element} element 8 | * @param {Number} offset 9 | * @param {State} state 10 | * @param {Editor} editor 11 | * @return {Object} 12 | */ 13 | 14 | function getPoint(element, offset, state, editor) { 15 | const { document } = state 16 | const schema = editor.getSchema() 17 | 18 | // If we can't find an offset key, we can't get a point. 19 | const offsetKey = OffsetKey.findKey(element, offset) 20 | if (!offsetKey) return null 21 | 22 | // COMPAT: If someone is clicking from one Slate editor into another, the 23 | // select event fires two, once for the old editor's `element` first, and 24 | // then afterwards for the correct `element`. (2017/03/03) 25 | const { key } = offsetKey 26 | const node = document.getDescendant(key) 27 | if (!node) return null 28 | 29 | const decorators = document.getDescendantDecorators(key, schema) 30 | const ranges = node.getRanges(decorators) 31 | const point = OffsetKey.findPoint(offsetKey, ranges) 32 | return point 33 | } 34 | 35 | /** 36 | * Export. 37 | * 38 | * @type {Function} 39 | */ 40 | 41 | export default getPoint 42 | -------------------------------------------------------------------------------- /src/lib/slate/utils/is-in-range.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Check if an `index` of a `text` node is in a `range`. 4 | * 5 | * @param {Number} index 6 | * @param {Text} text 7 | * @param {Selection} range 8 | * @return {Boolean} 9 | */ 10 | 11 | function isInRange(index, text, range) { 12 | const { startKey, startOffset, endKey, endOffset } = range 13 | 14 | if (text.key == startKey && text.key == endKey) { 15 | return startOffset <= index && index < endOffset 16 | } else if (text.key == startKey) { 17 | return startOffset <= index 18 | } else if (text.key == endKey) { 19 | return index < endOffset 20 | } else { 21 | return true 22 | } 23 | } 24 | 25 | /** 26 | * Export. 27 | * 28 | * @type {Function} 29 | */ 30 | 31 | export default isInRange 32 | -------------------------------------------------------------------------------- /src/lib/slate/utils/is-react-component.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Check if an `object` is a React component. 4 | * 5 | * @param {Object} object 6 | * @return {Boolean} 7 | */ 8 | 9 | function isReactComponent(object) { 10 | return ( 11 | object && 12 | object.prototype && 13 | object.prototype.isReactComponent 14 | ) 15 | } 16 | 17 | /** 18 | * Export. 19 | * 20 | * @type {Function} 21 | */ 22 | 23 | export default isReactComponent 24 | -------------------------------------------------------------------------------- /src/lib/slate/utils/noop.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Noop. 4 | * 5 | * @return {Void} 6 | */ 7 | 8 | function noop() {} 9 | 10 | /** 11 | * Export. 12 | * 13 | * @type {Function} 14 | */ 15 | 16 | export default noop 17 | -------------------------------------------------------------------------------- /src/lib/slate/utils/warn.js: -------------------------------------------------------------------------------- 1 | 2 | import IS_DEV from '../constants/is-dev' 3 | 4 | /** 5 | * Log a development warning. 6 | * 7 | * @param {String} message 8 | * @param {Any} ...args 9 | */ 10 | 11 | function warn(message, ...args) { 12 | if (!IS_DEV) { 13 | return 14 | } 15 | 16 | if (typeof console !== 'undefined') { 17 | console.warn(`Warning: ${message}`, ...args) // eslint-disable-line no-console 18 | } 19 | } 20 | 21 | /** 22 | * Export. 23 | * 24 | * @type {Function} 25 | */ 26 | 27 | export default warn 28 | -------------------------------------------------------------------------------- /src/mcode-pilot/components/SimilarPatientsSelector/SimilarPatientsSelector.scss: -------------------------------------------------------------------------------- 1 | @import '../../../styles/variables'; 2 | 3 | .similar-patients-selector { 4 | &__select-buttons { 5 | button { 6 | background-color: $background; 7 | border: 0; 8 | cursor: pointer; 9 | color: $interface-blue; 10 | font-size: 0.9em; 11 | font-weight: 600; 12 | 13 | &:hover { 14 | color: darken($interface-blue, 10%); 15 | } 16 | 17 | &:focus { 18 | outline: none; 19 | background-color: $interface-blue; 20 | color: #fff; 21 | border-radius: 5px; 22 | } 23 | } 24 | } 25 | 26 | .selection-option { 27 | display: flex; 28 | flex-direction: column; 29 | margin: 20px 0; 30 | } 31 | 32 | &__options { 33 | display: flex; 34 | justify-content: flex-start; 35 | flex-wrap: wrap; 36 | 37 | .options-checkbox-list { 38 | flex: 0 1 calc(33% - 1em); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/mcode-pilot/components/TreatmentOptionsOutcomesTable/CompareSelectedIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const CompareSelectedIcon = ({ onClick }) => { 5 | return ( 6 |
    7 | 8 | 13 | 14 | 19 | 20 |
    21 | ); 22 | }; 23 | 24 | CompareSelectedIcon.propTypes = { 25 | onClick: PropTypes.func 26 | }; 27 | 28 | export default CompareSelectedIcon; 29 | -------------------------------------------------------------------------------- /src/mcode-pilot/components/TreatmentOptionsOutcomesTable/PersonIcon.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const PersonIcon = (props) => { 5 | return ( 6 | 7 | 11 | 12 | ); 13 | }; 14 | 15 | PersonIcon.propTypes = { 16 | onClick: PropTypes.func 17 | }; 18 | 19 | export default PersonIcon; 20 | -------------------------------------------------------------------------------- /src/mcode-pilot/components/TreatmentOptionsSelector/TreatmentOptionsSelector.scss: -------------------------------------------------------------------------------- 1 | @import '../../../styles/variables'; 2 | 3 | div.treatment-options-selector { 4 | box-shadow: none; 5 | margin: 10px 0; 6 | 7 | &::before { 8 | background-color: transparent; 9 | } 10 | 11 | &__header { 12 | cursor: pointer; 13 | 14 | &-icon, 15 | &-title, 16 | &-subtitle { 17 | display: inline-block; 18 | } 19 | 20 | &-icon { 21 | font-size: 1.2em; 22 | color: $state; 23 | } 24 | 25 | &-title { 26 | font-weight: 600; 27 | font-size: 0.9em; 28 | } 29 | 30 | &-subtitle { 31 | font-size: 0.8em; 32 | margin-left: 15px; 33 | } 34 | 35 | &-subheader { 36 | font-size: 0.8em; 37 | margin-left: 35px; 38 | } 39 | 40 | .bold { font-weight: bold; } 41 | .italic { font-style: italic; } 42 | 43 | .muted { 44 | color: #888; 45 | margin-left: 10px; 46 | } 47 | } 48 | 49 | &__content { 50 | margin: 10px 25px; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/mcode-pilot/components/TreatmentsPopover/TreatmentsPopover.scss: -------------------------------------------------------------------------------- 1 | @import '../../../styles/variables'; 2 | 3 | .treatments-popper { 4 | position: absolute; 5 | top: 0; 6 | left: 0; 7 | min-width: 250px; 8 | z-index: 1; 9 | margin-bottom: 1rem; 10 | 11 | .form-control { 12 | display: flex; 13 | } 14 | 15 | .popover { 16 | padding: 10px 15px; 17 | font-size: 0.9em; 18 | } 19 | 20 | .popover-title { 21 | font-weight: bold; 22 | margin-bottom: 0.5rem; 23 | color: inherit; 24 | font-size: 0.9em; 25 | } 26 | 27 | .popover-checklist { 28 | margin-left: 20px; 29 | 30 | .form-control { 31 | white-space: nowrap; 32 | 33 | .checkbox { 34 | height: 22px; 35 | color: $shr-context-dark; 36 | margin-right: -5px; 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/mcode-pilot/containers/TreatmentOptionsVisualizer/TreatmentOptionsVisualizer.scss: -------------------------------------------------------------------------------- 1 | div.treatment-options-visualizer { 2 | max-width: 1200px; 3 | 4 | &__info { 5 | font-size: 0.8em; 6 | margin: 5px 25px; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/mcode-pilot/services/outcomes/IOutcomesService.js: -------------------------------------------------------------------------------- 1 | /* 2 | Interface spec for OutcomesService implementations. 3 | Initial version of the spec is dervived from the functionality currently in 4 | use by the TreatmentOptionsOutcomes container and supporting views. 5 | */ 6 | export default class IOutcomesService { 7 | 8 | /* 9 | Function to process similar patient outcomes. 10 | @param {Object} fOptions -- An object containing all information about the filters. 11 | @return {Object} returns the calculated outcome data 12 | similarPatientTreatments -- treatments that matched the similar patients [{key: String, name: String}] 13 | similarPatientTreatmentsData -- the outcomes for the similar patient treatments 14 | totalPatients -- total number of patients in the 15 | totalSimilarPatients -- total number of patients that match the cohort filtering criteria 16 | */ 17 | processSimilarPatientOutcomes(fOptions) { 18 | throw new Error("processSimilarPatientOutcomes not implemented by " + this.constructor.name); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/mcode-pilot/services/outcomes/StaticOutcomesService.js: -------------------------------------------------------------------------------- 1 | import { filterTreatmentData } from '../../utils/filterTreatmentData'; 2 | import IOutcomesService from './IOutcomesService'; 3 | 4 | export default class StaticOutcomesService extends IOutcomesService { 5 | constructor(config) { 6 | super(); 7 | this.timescale = config.timescale; 8 | this.filters = config.filters; 9 | this.showSideEffects = config.showSideEffects; 10 | } 11 | 12 | async processSimilarPatientOutcomes(fOptions) { 13 | return filterTreatmentData(fOptions, this.timescale); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/mcode-pilot/utils/numberWithCommas.js: -------------------------------------------------------------------------------- 1 | export default function numberWithCommas(x) { 2 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); 3 | } 4 | -------------------------------------------------------------------------------- /src/mcode-pilot/utils/sideEffects.js: -------------------------------------------------------------------------------- 1 | export default function getSideEffects(similarPatientTreatmentsData) { 2 | const allTreatmentData = [...similarPatientTreatmentsData]; 3 | const allEffects = allTreatmentData.map(effect => Object.keys(effect.sideEffects.effects)); 4 | 5 | return allEffects.reduce((p, c) => { 6 | // concat the arrays, filter out duplicate entries 7 | return p.concat(c.filter(cx => p.indexOf(cx) < 0)); 8 | }, []).sort(function (a, b) { 9 | return a.toLowerCase().localeCompare(b.toLowerCase()); 10 | }); 11 | }; 12 | -------------------------------------------------------------------------------- /src/model/FluxObjectFactory.js: -------------------------------------------------------------------------------- 1 | import { getNamespaceAndName, uuid } from './json-helper'; 2 | import ObjectFactory from './ObjectFactory'; 3 | import FluxOncocoreObjectFactory from './fluxWrappers/onco/core/FluxOncocoreObjectFactory'; 4 | import FluxCoreObjectFactory from './fluxWrappers/core/FluxCoreObjectFactory'; 5 | 6 | /* 7 | * FluxObjectFactory class returns instances of Flux model classes 8 | * Default case will return SHR model classes if no Flux wrapper class is found 9 | */ 10 | export default class FluxObjectFactory { 11 | static createInstance(json, type, patientRecord) { 12 | const { namespace } = getNamespaceAndName(json, type); 13 | switch (namespace) { 14 | case 'shr.core': return FluxCoreObjectFactory.createInstance(json, type, patientRecord); 15 | case 'onco.core': return FluxOncocoreObjectFactory.createInstance(json, type, patientRecord); 16 | default: return ObjectFactory.createInstance(json, type, patientRecord); 17 | } 18 | } 19 | 20 | static createInstanceFromFHIR(shrType, fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { 21 | return ObjectFactory.createInstanceFromFHIR(shrType, fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/model/fluxExtensions/EntryFix.js: -------------------------------------------------------------------------------- 1 | import Entry from '../shr/base/Entry'; 2 | 3 | /** 4 | * This fix class adds SourceClinicalNote fields to the shr.base.Entry classes. 5 | */ 6 | export default class EntryFix extends Entry { 7 | /** 8 | * Get the SourceClinicalNote. 9 | * @returns {SourceClinicalNote} The shr.base.SourceClinicalNote 10 | */ 11 | get sourceClinicalNote() { 12 | return this._sourceClinicalNote; 13 | } 14 | 15 | /** 16 | * Set the SourceClinicalNote. 17 | * @param {SourceClinicalNote} sourceClinicalNote - The shr.base.SourceClinicalNote 18 | */ 19 | set sourceClinicalNote(sourceClinicalNote) { 20 | this._sourceClinicalNote = sourceClinicalNote; 21 | } 22 | 23 | /** 24 | * Serializes an instance of the Entry class to a JSON object. 25 | * The JSON is expected to be valid against the Entry JSON schema, but no validation checks are performed. 26 | * @returns {object} a JSON object populated with the data from the element 27 | */ 28 | toJSON() { 29 | const inst = super.toJSON() 30 | if (this.sourceClinicalNote != null) { 31 | inst['SourceClinicalNote'] = typeof this.sourceClinicalNote.toJSON === 'function' ? this.sourceClinicalNote.toJSON() : this.sourceClinicalNote; 32 | } 33 | return inst; 34 | } 35 | } -------------------------------------------------------------------------------- /src/model/fluxExtensions/ExpectedPerformanceTimeFix.js: -------------------------------------------------------------------------------- 1 | import ExpectedPerformanceTime from "../shr/core/ExpectedPerformanceTime"; 2 | import { FHIRHelper, uuid } from '../json-helper'; 3 | 4 | export default class ExpectedPerformanceTimeFix extends ExpectedPerformanceTime { 5 | static fromFHIR(fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { 6 | const inst = new ExpectedPerformanceTimeFix(); 7 | if (!asExtension && fhir != null) { 8 | // reminder: ExpectedPerformanceTime.value can be: (dateTime|TimePeriod|Timing) 9 | 10 | switch(fhirType) { 11 | case 'dateTime': 12 | inst.value = fhir; 13 | break; 14 | 15 | case 'Timing': 16 | inst.value = FHIRHelper.createInstanceFromFHIR('shr.core.Timing', fhir, 'Timing', shrId, allEntries, mappedResources, referencesOut); 17 | break; 18 | 19 | // the one instance here where the FHIR type != the SHR type name.... 20 | case 'Period': 21 | inst.value = FHIRHelper.createInstanceFromFHIR('shr.core.TimePeriod', fhir, 'Period', shrId, allEntries, mappedResources, referencesOut); 22 | break; 23 | 24 | default: 25 | // do nothing 26 | } 27 | } 28 | return inst; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/model/fluxExtensions/MedicationRequestFix.js: -------------------------------------------------------------------------------- 1 | import MedicationRequest from '../shr/core/MedicationRequest'; 2 | import { FHIRHelper, uuid } from '../json-helper'; 3 | 4 | /** 5 | * This fix class adds a reasonReference mapping to the shr.core.MedicationRequest class fromFHIR method. 6 | */ 7 | export default class MedicationRequestFix extends MedicationRequest { 8 | 9 | static fromFHIR(fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { 10 | const inst = super.fromFHIR(fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension); 11 | 12 | // reasonReference is in the spec and ES6 class but not mapped in fromFHIR for some reason. need to review 13 | if (fhir['reasonReference'] != null) { 14 | inst.reasonReference = inst.reasonReference || []; 15 | const inst_reason = FHIRHelper.createInstanceFromFHIR('shr.core.ReasonReference', fhir['reasonReference'], 'Reference', shrId, allEntries, mappedResources, referencesOut, false); 16 | inst.reasonReference.push(inst_reason); 17 | } 18 | 19 | return inst; 20 | } 21 | } -------------------------------------------------------------------------------- /src/model/fluxExtensions/PrimaryCancerConditionFix.js: -------------------------------------------------------------------------------- 1 | import PrimaryCancerCondition from '../onco/core/PrimaryCancerCondition'; 2 | import { FHIRHelper, uuid } from '../json-helper'; 3 | 4 | /** 5 | * This fix class replaces the original onco.core.PrimaryCancerCondition.fromFHIR function 6 | * with a version that handles the extension.valueReference correctly. 7 | * 8 | * See also: https://github.com/standardhealth/shr-es6-export/issues/57 9 | */ 10 | export default class PrimaryCancerConditionFix extends PrimaryCancerCondition { 11 | 12 | static fromFHIR(fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { 13 | if (asExtension && fhir['valueReference']) { 14 | 15 | const entryId = fhir['valueReference']['reference']; 16 | if (!mappedResources[entryId]) { 17 | const referencedEntry = allEntries.find(e => e.fullUrl === entryId); 18 | if (referencedEntry) { 19 | mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR('onco.core.PrimaryCancerCondition', referencedEntry['resource'], 'undefined', shrId, allEntries, mappedResources, referencesOut); 20 | } 21 | } 22 | return mappedResources[entryId]; 23 | 24 | } else { 25 | return super.fromFHIR(fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/model/fluxExtensions/ReasonReferenceFix.js: -------------------------------------------------------------------------------- 1 | import ReasonReference from '../shr/core/ReasonReference'; 2 | import { FHIRHelper, uuid } from '../json-helper'; 3 | 4 | /** 5 | * This fix class replaces the original shr.core.ReasonReference.fromFHIR function 6 | * with a version that sets the value correctly as a reference instead of a full object. 7 | */ 8 | export default class ReasonReferenceFix extends ReasonReference { 9 | 10 | static fromFHIR(fhir, fhirType, shrId=uuid(), allEntries=[], mappedResources={}, referencesOut=[], asExtension=false) { 11 | if (!asExtension && fhir['reference']) { 12 | const inst = new ReasonReferenceFix(); 13 | 14 | const entryId = fhir['reference']; 15 | if (!mappedResources[entryId]) { 16 | const referencedEntry = allEntries.find(e => e.fullUrl === entryId); 17 | if (referencedEntry) { 18 | mappedResources[entryId] = FHIRHelper.createInstanceFromFHIR(null, referencedEntry['resource'], 'undefined', shrId, allEntries, mappedResources, referencesOut); 19 | } 20 | } 21 | if (mappedResources[entryId]) { 22 | inst.value = FHIRHelper.createReference(mappedResources[entryId], referencesOut); 23 | } 24 | // no else in this case since we don't know which of the 2 possible types it is 25 | 26 | return inst; 27 | } else { 28 | return super.fromFHIR(fhir, fhirType, shrId, allEntries, mappedResources, referencesOut, asExtension); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/model/fluxWrappers/base/FluxEntry.js: -------------------------------------------------------------------------------- 1 | import ClassRegistry from '../../ClassRegistry'; 2 | 3 | class FluxEntry { 4 | get sourceClinicalNoteReference() { 5 | return this._entry.entryInfo.sourceClinicalNote; 6 | } 7 | 8 | get entryInfo() { 9 | return this._entry.entryInfo; 10 | } 11 | 12 | set entryInfo(entryInfo) { 13 | this._entry.entryInfo = entryInfo; 14 | } 15 | 16 | toJSON() { 17 | return this._entry.toJSON(); 18 | } 19 | 20 | /** 21 | * Extract a human-readable string from a code. 22 | * 23 | * @param {Coding} coding 24 | * @returns {string} the display text if available, otherwise the code. 25 | * @private 26 | */ 27 | _displayTextOrCode(coding) { 28 | if (!coding) return null; 29 | if (coding.displayText) return coding.displayText.value; 30 | if (coding.codeValue) return coding.codeValue.value; 31 | return null; 32 | } 33 | 34 | /** 35 | * Helper function to construct an entry with the given URI as the entry type value. 36 | */ 37 | _constructEntry(uri) { 38 | const Entry = ClassRegistry.get('shr.base', 'Entry'); 39 | const EntryType = ClassRegistry.get('shr.base', 'EntryType'); 40 | 41 | let entry = new Entry(); 42 | entry.entryType = new EntryType(); 43 | entry.entryType.uri = uri; 44 | return entry; 45 | } 46 | } 47 | 48 | export default FluxEntry; 49 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxBloodPressure.js: -------------------------------------------------------------------------------- 1 | import BloodPressure from "../../shr/core/BloodPressure"; 2 | 3 | class FluxBloodPressure { 4 | constructor(json) { 5 | this._bloodPressure = BloodPressure.fromJSON(json); 6 | } 7 | 8 | get code() { 9 | return this._bloodPressure.code.value.coding[0].codeValue.code; 10 | } 11 | 12 | get dataValue() { 13 | return this._bloodPressure.dataValue; 14 | } 15 | 16 | get entryInfo() { 17 | return this._bloodPressure.entryInfo; 18 | } 19 | 20 | get relevantTime() { 21 | return this._bloodPressure.relevantTime.value; 22 | } 23 | 24 | get value() { 25 | return this._bloodPressure.components.map(r => r.dataValue.value.number.decimal).join('/'); 26 | } 27 | 28 | toJSON() { 29 | return this._bloodPressure.toJSON(); 30 | } 31 | } 32 | 33 | export default FluxBloodPressure; 34 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxBodyTemperature.js: -------------------------------------------------------------------------------- 1 | import BodyTemperature from '../../shr/core/BodyTemperature'; 2 | 3 | class FluxBodyTemperature { 4 | constructor(json) { 5 | this._bodyTemperature = BodyTemperature.fromJSON(json); 6 | } 7 | 8 | get code() { 9 | return this._bodyTemperature.code.value.coding[0].codeValue.code; 10 | } 11 | 12 | get entryInfo() { 13 | return this._bodyTemperature.entryInfo; 14 | } 15 | 16 | get relevantTime() { 17 | return this._bodyTemperature.relevantTime.value; 18 | } 19 | 20 | get units() { 21 | return this._bodyTemperature.dataValue.value.units.coding.codeValue.value; 22 | } 23 | 24 | get value() { 25 | return this._bodyTemperature.dataValue.value.number.decimal; 26 | } 27 | 28 | toJSON() { 29 | return this._bodyTemperature.toJSON(); 30 | } 31 | } 32 | 33 | export default FluxBodyTemperature; 34 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxBodyWeight.js: -------------------------------------------------------------------------------- 1 | import BodyWeight from '../../shr/core/BodyWeight'; 2 | 3 | class FluxBodyWeight { 4 | constructor(json) { 5 | this._bodyWeight = BodyWeight.fromJSON(json); 6 | } 7 | 8 | get code() { 9 | return this._bodyWeight.code.value.coding[0].codeValue.code; 10 | } 11 | 12 | get entryInfo() { 13 | return this._bodyWeight.entryInfo; 14 | } 15 | 16 | get relevantTime() { 17 | return this._bodyWeight.relevantTime.value; 18 | } 19 | 20 | get units() { 21 | return this._bodyWeight.dataValue.value.units.coding.codeValue.value; 22 | } 23 | 24 | get value() { 25 | return this._bodyWeight.dataValue.value.number.decimal; 26 | } 27 | 28 | toJSON() { 29 | return this._bodyWeight.toJSON(); 30 | } 31 | } 32 | 33 | export default FluxBodyWeight; 34 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxDiagnosticReport.js: -------------------------------------------------------------------------------- 1 | import DiagnosticReport from '../../shr/core/DiagnosticReport'; 2 | import FluxEntry from '../base/FluxEntry'; 3 | 4 | class FluxDiagnosticReport extends FluxEntry { 5 | constructor(json, patientRecord) { 6 | super(); 7 | 8 | this._diagnosticReport = this._entry = DiagnosticReport.fromJSON(json); 9 | this._patientRecord = patientRecord; 10 | } 11 | 12 | get relevantTime() { 13 | return this._diagnosticReport.relevantTime.value; 14 | } 15 | 16 | get value() { 17 | return this._patientRecord.getEntryFromReference(this._diagnosticReport.media); 18 | } 19 | 20 | get author() { 21 | if (this._diagnosticReport.participation && this._diagnosticReport.participation.participant && this._diagnosticReport.participation.participant.value) { 22 | const author = this._patientRecord.getEntryFromReference(this._diagnosticReport.participation.participant.value); 23 | 24 | if (author 25 | && author.person 26 | && author.person.humanName 27 | && author.person.humanName[0] 28 | && author.person.humanName[0].nameAsText) { 29 | return author.person.humanName[0].nameAsText.value; 30 | } 31 | } 32 | 33 | return null; 34 | } 35 | } 36 | export default FluxDiagnosticReport; 37 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxECOGPerformanceStatus.js: -------------------------------------------------------------------------------- 1 | import FluxEntry from '../base/FluxEntry'; 2 | import ECOGPerformanceStatus from '../../shr/core/ECOGPerformanceStatus'; 3 | 4 | class FluxECOGPerformanceStatus extends FluxEntry { 5 | constructor(json, type, patientRecord) { 6 | super(); 7 | this._patientRecord = patientRecord; 8 | this._entry = ECOGPerformanceStatus.fromJSON(json); 9 | } 10 | 11 | get entryInfo() { 12 | return this._entry.entryInfo; 13 | } 14 | 15 | } 16 | 17 | export default FluxECOGPerformanceStatus; 18 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxEncounter.js: -------------------------------------------------------------------------------- 1 | import Encounter from '../../shr/core/Encounter'; 2 | 3 | class FluxEncounter { 4 | constructor(json) { 5 | this._encounter = Encounter.fromJSON(json); 6 | } 7 | 8 | get entryInfo() { 9 | return this._encounter.entryInfo; 10 | } 11 | 12 | get timePeriod() { 13 | return this._encounter.timePeriod; 14 | } 15 | 16 | toJSON() { 17 | return this._encounter.toJSON(); 18 | } 19 | } 20 | 21 | export default FluxEncounter; -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxHeartRate.js: -------------------------------------------------------------------------------- 1 | import HeartRate from '../../shr/core/HeartRate'; 2 | 3 | class FluxHeartRate { 4 | constructor(json) { 5 | this._heartRate = HeartRate.fromJSON(json); 6 | } 7 | 8 | get code() { 9 | return this._heartRate.code.value.coding[0].codeValue.code; 10 | } 11 | 12 | get entryInfo() { 13 | return this._heartRate.entryInfo; 14 | } 15 | 16 | get relevantTime() { 17 | return this._heartRate.relevantTime.value; 18 | } 19 | 20 | get units() { 21 | return this._heartRate.dataValue.value.units.coding.codeValue.value; 22 | } 23 | 24 | get value() { 25 | return this._heartRate.dataValue.value.number.decimal; 26 | } 27 | 28 | toJSON() { 29 | return this._heartRate.toJSON(); 30 | } 31 | } 32 | 33 | export default FluxHeartRate; 34 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxKarnofskyPerformanceStatus.js: -------------------------------------------------------------------------------- 1 | import FluxEntry from '../base/FluxEntry'; 2 | import KarnofskyPerformanceStatus from '../../shr/core/KarnofskyPerformanceStatus'; 3 | 4 | class FluxKarnofskyPerformanceStatus extends FluxEntry { 5 | constructor(json, type, patientRecord) { 6 | super(); 7 | this._patientRecord = patientRecord; 8 | this._entry = KarnofskyPerformanceStatus.fromJSON(json); 9 | } 10 | 11 | get entryInfo() { 12 | return this._entry.entryInfo; 13 | } 14 | 15 | } 16 | 17 | export default FluxKarnofskyPerformanceStatus; 18 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxQuestionnaireResponse.js: -------------------------------------------------------------------------------- 1 | import QuestionnaireResponse from "../../shr/core/QuestionnaireResponse"; 2 | import FluxQuestionnaireResponseItem from "./FluxQuestionnaireResponseItem"; 3 | 4 | class FluxQuestionnaireResponse { 5 | constructor(json) { 6 | this._questionnaireResponse = QuestionnaireResponse.fromJSON(json); 7 | this._questionnaireResponseItem = this._questionnaireResponse.questionnaireResponseItem.map(q => new FluxQuestionnaireResponseItem(q)); 8 | } 9 | 10 | get entryInfo() { 11 | return this._questionnaireResponse.entryInfo; 12 | } 13 | 14 | get members() { 15 | return this._questionnaireResponseItem; 16 | } 17 | 18 | get statementDateTime() { 19 | return this._questionnaireResponse.statementDateTime.value; 20 | } 21 | } 22 | 23 | export default FluxQuestionnaireResponse; 24 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/core/FluxQuestionnaireResponseItem.js: -------------------------------------------------------------------------------- 1 | class FluxQuestionnaireResponseItem { 2 | constructor(questionnaireResponseItem) { 3 | this._questionnaireResponseItem = questionnaireResponseItem; 4 | } 5 | 6 | get questionText() { 7 | return this._questionnaireResponseItem.question.value; 8 | } 9 | 10 | get value() { 11 | // TODO: We are assuming Answer array only has one element 12 | return this._questionnaireResponseItem.answer[0].answerValue.value; 13 | } 14 | } 15 | 16 | export default FluxQuestionnaireResponseItem; 17 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxEvidenceType.js: -------------------------------------------------------------------------------- 1 | import EvidenceType from '../../../onco/core/EvidenceType'; 2 | 3 | export default class FluxEvidenceType { 4 | constructor(json) { 5 | this._evidenceType = EvidenceType.fromJSON(json); 6 | } 7 | 8 | get value() { 9 | return this._evidenceType.value.coding[0].displayText.value 10 | } 11 | 12 | set value(value) { 13 | this._evidenceType.value = value; 14 | } 15 | 16 | toJSON() { 17 | return this._evidenceType.toJSON(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxGenomicsReport.js: -------------------------------------------------------------------------------- 1 | import GenomicsReport from "../../../onco/core/GenomicsReport"; 2 | import FluxEntry from "../../base/FluxEntry"; 3 | 4 | class FluxGenomicsReport extends FluxEntry { 5 | constructor(json, patientRecord) { 6 | super(); 7 | 8 | this._genomicsReport = this._entry = GenomicsReport.fromJSON(json); 9 | this._patientRecord = patientRecord; 10 | } 11 | 12 | get relevantTime() { 13 | return this._genomicsReport.relevantTime.value; 14 | } 15 | 16 | get members() { 17 | return this._genomicsReport.observation.map(o => this._patientRecord.getEntryFromReference(o)); 18 | } 19 | } 20 | 21 | export default FluxGenomicsReport; 22 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxTNMClinicalDistantMetastasesCategory.js: -------------------------------------------------------------------------------- 1 | import TNMClinicalDistantMetastasesCategory from '../../../onco/core/TNMClinicalDistantMetastasesCategory'; 2 | import FluxCancerStageCategory from './FluxCancerStageCategory'; 3 | 4 | class FluxTNMClinicalDistantMetastasesCategory extends FluxCancerStageCategory { 5 | constructor(json) { 6 | super(json); 7 | this._cancerStageCategory = TNMClinicalDistantMetastasesCategory.fromJSON(json); 8 | if (!this._cancerStageCategory.entryInfo) { 9 | this._cancerStageCategory.entryInfo = this._constructEntry('http://standardhealthrecord.org/spec/onco/core/TNMClinicalDistantMetastasesCategory'); 10 | } 11 | } 12 | } 13 | 14 | export default FluxTNMClinicalDistantMetastasesCategory; 15 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxTNMClinicalPrimaryTumorCategory.js: -------------------------------------------------------------------------------- 1 | import FluxCancerStageCategory from './FluxCancerStageCategory'; 2 | import TNMClinicalPrimaryTumorCategory from '../../../onco/core/TNMClinicalPrimaryTumorCategory'; 3 | 4 | class FluxTNMClinicalPrimaryTumorCategory extends FluxCancerStageCategory { 5 | constructor(json) { 6 | super(json); 7 | this._cancerStageCategory = TNMClinicalPrimaryTumorCategory.fromJSON(json); 8 | if (!this._cancerStageCategory.entryInfo) { 9 | this._cancerStageCategory.entryInfo = this._constructEntry('http://standardhealthrecord.org/spec/onco/core/TNMClinicalPrimaryTumorCategory'); 10 | } 11 | } 12 | } 13 | 14 | export default FluxTNMClinicalPrimaryTumorCategory; 15 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxTNMClinicalRegionalNodesCategory.js: -------------------------------------------------------------------------------- 1 | import FluxCancerStageCategory from './FluxCancerStageCategory'; 2 | import TNMClinicalRegionalNodesCategory from '../../../onco/core/TNMClinicalRegionalNodesCategory'; 3 | 4 | class FluxTNMClinicalRegionalNodesCategory extends FluxCancerStageCategory { 5 | constructor(json) { 6 | super(json); 7 | this._cancerStageCategory = TNMClinicalRegionalNodesCategory.fromJSON(json); 8 | if (!this._cancerStageCategory.entryInfo) { 9 | this._cancerStageCategory.entryInfo = this._constructEntry('http://standardhealthrecord.org/spec/onco/core/TNMClinicalRegionalNodesCategory'); 10 | } 11 | } 12 | } 13 | 14 | export default FluxTNMClinicalRegionalNodesCategory; 15 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxTNMClinicalStageGroup.js: -------------------------------------------------------------------------------- 1 | import TNMClinicalStageGroup from '../../../onco/core/TNMClinicalStageGroup'; 2 | import PanelMembers from '../../../shr/core/PanelMembers'; 3 | import FluxTNMStageGroup from './FluxTNMStageGroup'; 4 | import moment from 'moment'; 5 | 6 | class FluxTNMClinicalStageGroup extends FluxTNMStageGroup { 7 | constructor(json, patientRecord) { 8 | super(); 9 | this._entry = this._tnmStageGroup = TNMClinicalStageGroup.fromJSON(json); 10 | if (!this._tnmStageGroup.entryInfo) { 11 | this._tnmStageGroup.entryInfo = this._constructEntry('http://standardhealthrecord.org/spec/onco/core/TNMClinicalStageGroup'); 12 | this._tnmStageGroup.panelMembers = new PanelMembers(); 13 | this._tnmStageGroup.panelMembers.observation = []; 14 | } 15 | if (!this._tnmStageGroup.relevantTime) { 16 | const today = new moment().format('D MMM YYYY'); 17 | this.relevantTime = today; 18 | } 19 | this._patientRecord = patientRecord; 20 | } 21 | } 22 | 23 | export default FluxTNMClinicalStageGroup; 24 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/onco/core/FluxTNMPathologicStageGroup.js: -------------------------------------------------------------------------------- 1 | import TNMPathologicStageGroup from '../../../onco/core/TNMPathologicStageGroup'; 2 | import PanelMembers from '../../../shr/core/PanelMembers'; 3 | import FluxTNMStageGroup from './FluxTNMStageGroup'; 4 | 5 | class FluxTNMPathologicStageGroup extends FluxTNMStageGroup { 6 | constructor(json, patientRecord) { 7 | super(); 8 | this._entry = this._tnmStageGroup = TNMPathologicStageGroup.fromJSON(json); 9 | if (!this._tnmStageGroup.entryInfo) { 10 | this._tnmStageGroup.entryInfo = this._constructEntry('http://standardhealthrecord.org/spec/onco/core/TNMPathologicStageGroup'); 11 | this._tnmStageGroup.panelMembers = new PanelMembers(); 12 | this._tnmStageGroup.panelMembers.observation = []; 13 | } 14 | this._patientRecord = patientRecord; 15 | } 16 | } 17 | 18 | export default FluxTNMPathologicStageGroup; 19 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/oncocore/FluxCancerHistologicType.js: -------------------------------------------------------------------------------- 1 | // import CancerHistologicType from '../oncocore/CancerHistologicType'; 2 | import FluxObservation from '../core/FluxObservation'; 3 | 4 | class FluxCancerHistologicType extends FluxObservation { 5 | constructor(json) { 6 | super(); 7 | // this._histologicType = this._observation = this._entry = CancerHistologicType.fromJSON(json); 8 | } 9 | 10 | get entryInfo() { 11 | return this._histologicType.entryInfo; 12 | } 13 | 14 | /** 15 | * Getter for type 16 | * This will return the displayText string from CodeableConcept Value 17 | */ 18 | get type() { 19 | return this._displayTextOrCode(this._histologicType.value.coding[0]); 20 | } 21 | 22 | toJSON() { 23 | return this._histologicType.toJSON(); 24 | } 25 | } 26 | 27 | export default FluxCancerHistologicType; -------------------------------------------------------------------------------- /src/model/fluxWrappers/tumor/FluxTumorDimensions.js: -------------------------------------------------------------------------------- 1 | import TumorDimensions from '../../onco/core/TumorDimensions'; 2 | import FluxObservation from '../core/FluxObservation'; 3 | 4 | class FluxTumorDimensions extends FluxObservation { 5 | constructor(json) { 6 | super(); 7 | this._tumorDimensions = this._observation = this._entry = TumorDimensions.fromJSON(json); 8 | } 9 | 10 | get entryInfo() { 11 | return this._tumorDimensions.entryInfo; 12 | } 13 | 14 | toJSON() { 15 | return this._tumorDimensions.toJSON(); 16 | } 17 | } 18 | 19 | export default FluxTumorDimensions; 20 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/tumor/FluxTumorMargins.js: -------------------------------------------------------------------------------- 1 | // import TumorMargins from './TumorMargins'; 2 | import FluxObservation from '../core/FluxObservation'; 3 | 4 | class FluxTumorMargins extends FluxObservation { 5 | constructor(json, patientRecord) { 6 | super(); 7 | this._patientRecord = patientRecord; 8 | // this._tumorMargins = this._observation = this._entry = TumorMargins.fromJSON(json); 9 | } 10 | 11 | get entryInfo() { 12 | return this._tumorMargins.entryInfo; 13 | } 14 | 15 | get value() { 16 | return this._tumorMargins.findingResult.value.coding[0].displayText.value; 17 | } 18 | } 19 | 20 | export default FluxTumorMargins; 21 | -------------------------------------------------------------------------------- /src/model/fluxWrappers/tumor/FluxTumorObjectFactory.js: -------------------------------------------------------------------------------- 1 | import { getNamespaceAndName } from '../../json-helper'; 2 | import FluxTumorDimensions from './FluxTumorDimensions'; 3 | // import TumorObjectFactory from './TumorObjectFactory'; 4 | import FluxTumorMargins from './FluxTumorMargins'; 5 | 6 | export default class FluxTumorObjectFactory { 7 | static createInstance(json, type, patientRecord) { 8 | const { namespace, elementName } = getNamespaceAndName(json, type); 9 | if (namespace !== 'tumor') { 10 | throw new Error(`Unsupported type in TumorObjectFactory: ${type}`); 11 | } 12 | 13 | // returns Flux wrapper class if found, otherwise use ShrTumorObjectFactory 14 | switch (elementName) { 15 | case 'TumorDimensions': return new FluxTumorDimensions(json, patientRecord); 16 | case 'TumorMargins': return new FluxTumorMargins(json, patientRecord); 17 | // default: return TumorObjectFactory.createInstance(json, type); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/nav/NavBar.css: -------------------------------------------------------------------------------- 1 | .navbar-custom { 2 | background-color: #FFFFFF !important; 3 | } 4 | -------------------------------------------------------------------------------- /src/noteparser/samples/note1.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is presenting with carcinoma of the breast. #staging assessed as tumor size T2 and N0 + M0. -------------------------------------------------------------------------------- /src/noteparser/samples/note10.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is a 51 year old female is part of #enrollment #PATINA -------------------------------------------------------------------------------- /src/noteparser/samples/note11.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is #deceased on #10/01/2017 -------------------------------------------------------------------------------- /src/noteparser/samples/note12.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is presenting with carcinoma of the breast. 2 | 3 | #ER #Positive #PR #Negative #HER2 #Positive -------------------------------------------------------------------------------- /src/noteparser/samples/note13.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is a 51 year old female who #unenrolled from #PATINA on #09/04/2017 -------------------------------------------------------------------------------- /src/noteparser/samples/note14.txt: -------------------------------------------------------------------------------- 1 | #stop medication @active medication[[ibuprofen 600mg tablet]] -------------------------------------------------------------------------------- /src/noteparser/samples/note2.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is presenting with carcinoma of the breast. 2 | 3 | #toxicity #nausea #grade 2 #treatment -------------------------------------------------------------------------------- /src/noteparser/samples/note3.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is presenting with carcinoma of the breast. 2 | 3 | #disease status #stable based on #imaging and #physical exam -------------------------------------------------------------------------------- /src/noteparser/samples/note4.txt: -------------------------------------------------------------------------------- 1 | Invasive ductal carcinoma of breast 2 | 3 | #staging #T2 #N0 #M0 4 | 5 | #disease status #Stable #Pathology and #Symptoms 6 | 7 | #toxicity #Thrombotic thrombocytopenic purpura #Grade 3 attributed to #Disease -------------------------------------------------------------------------------- /src/noteparser/samples/note5.txt: -------------------------------------------------------------------------------- 1 | testing. unknown patient. 2 | 3 | #disease status is #Complete Response based on #Physical exam, #Markers, #Pathology 4 | 5 | more text. more stuff. no extra hash tags! -------------------------------------------------------------------------------- /src/noteparser/samples/note6.txt: -------------------------------------------------------------------------------- 1 | #toxicity of #Grade 3 #Blood and lymphatic system disorders - Other, specify related to #Unrelated and due to @active medication[[ibuprofen 600mg tablet]] -------------------------------------------------------------------------------- /src/noteparser/samples/note7.txt: -------------------------------------------------------------------------------- 1 | #toxicity of #not a valid phrase #Grade 3 #Blood and lymphatic system disorders - Other, specify related to #Unrelated -------------------------------------------------------------------------------- /src/noteparser/samples/note8.txt: -------------------------------------------------------------------------------- 1 | testing. unknown patient. 2 | 3 | #disease status is #Complete Response based on #Physical exam, #Markers, #Pathology #as of #10/5/2017 #reference date #5/2/2017 4 | 5 | more text. more stuff. no extra hash tags! -------------------------------------------------------------------------------- /src/noteparser/samples/note9.txt: -------------------------------------------------------------------------------- 1 | Debra Hernandez672 is a 51 year old female who consented to #enrollment #PATINA on #09/04/2017 -------------------------------------------------------------------------------- /src/notes/ActiveContextsBreadcrumbs.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .toolbar-breadcrumbs-container { 4 | color: $state; 5 | font-size: $size-m; 6 | font-style: italic; 7 | display: inline-block; 8 | vertical-align: bottom; 9 | max-width: calc(100% - 315px); 10 | } 11 | 12 | .breadcrumbs-container:after { 13 | content:"..."; 14 | background-color: white; 15 | color: transparent; 16 | position: relative; 17 | z-index: 2; 18 | } 19 | 20 | .breadcrumbs-container { 21 | margin-right: 1px; 22 | vertical-align: bottom; 23 | direction: rtl; 24 | display: inline-block; 25 | width: 100%; 26 | white-space: nowrap; 27 | overflow: hidden; 28 | position: relative; 29 | z-index: 3; 30 | } 31 | 32 | .breadcrumbs-container:before { 33 | content:"..."; 34 | background-color: white; 35 | position: absolute; 36 | left: 0; 37 | z-index: 1; 38 | } 39 | 40 | .breadcrumbs { 41 | display: inline-block; 42 | } 43 | 44 | .breadcrumb { 45 | display: inline; 46 | } 47 | 48 | .breadcrumb-separator { 49 | display: inline; 50 | padding: 0px 10px; 51 | } 52 | -------------------------------------------------------------------------------- /src/notes/CompletionComponentFactory.js: -------------------------------------------------------------------------------- 1 | import ContextListOptions from '../context/ContextListOptions'; 2 | import ContextGetHelp from '../context/ContextGetHelp'; 3 | import ContextCalendar from '../context/ContextCalendar'; 4 | 5 | export default class CompletionComponentFactory { 6 | static createInstance(completionComponentName) { 7 | // Can't do an instance of with a switch 8 | switch (completionComponentName) { 9 | case "number": 10 | console.error(`We don't currently support a completion component for ${completionComponentName}-subtypes; trying to get a completionComponent for ${completionComponentName}`); 11 | return null; 12 | case "date": 13 | return ContextCalendar; 14 | case "multi-choice": 15 | case "choice": 16 | return ContextListOptions; 17 | case "menu": 18 | return ContextGetHelp; 19 | default: 20 | console.error(`We don't currently support a completion component for ${completionComponentName}-subtypes; trying to get a completionComponent for ${completionComponentName}`); 21 | return null; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/notes/KeyboardShortcutsPlugin.js: -------------------------------------------------------------------------------- 1 | export default function KeyboardShortcutsPlugin() { 2 | const onKeyDown = (e, data, state, editor) => { 3 | if (data.isCmd || data.isCtrl) { 4 | let style = ""; 5 | if (data.key === "b") { 6 | style = "bold"; 7 | } else if (data.key === "i") { 8 | style = "italic"; 9 | } else if (data.key === "u") { 10 | style = "underlined"; 11 | } else { 12 | return state; 13 | } 14 | return state 15 | .transform() 16 | .toggleMark(style) 17 | .apply(); 18 | } 19 | }; 20 | 21 | return { 22 | onKeyDown 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /src/notes/PointOfCare.css: -------------------------------------------------------------------------------- 1 | #poc-panel { 2 | width: 100%; 3 | margin-top: 15px; 4 | overflow-x: hidden; 5 | overflow-y: scroll; 6 | /* 96px is the height of the Patient Control Panel, 20px is the height of the top margin of post encounter view 7 | content and a little extra for potential scroll bars, 82px accounts for the editor header */ 8 | height: calc(100vh - 96px - 20px - 82px); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/panels/NotesPanel.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | #finish-sign-component { 4 | position: fixed; 5 | bottom: 0px; 6 | width: calc(35vw); 7 | background-color: #ffffff; 8 | text-align: left; 9 | z-index: 5; 10 | display: flex; 11 | justify-content: left; 12 | .btn-finish { 13 | background-color: #FFFFFF; 14 | text-transform: none; 15 | display: inline-block; 16 | min-width: 90%; 17 | margin: 10px 30px 10px 30px; 18 | border: 1px solid $line-gray; 19 | border-radius: 3px; 20 | } 21 | } 22 | .panel-content.note-assistant-panel { 23 | padding: 0px !important; 24 | } 25 | -------------------------------------------------------------------------------- /src/panels/PatientControlPanel.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .patient-control-panel { 4 | width: 100vw; 5 | position: relative; 6 | text-align: left; 7 | left: 50%; 8 | right: 50%; 9 | margin-left: -50vw; 10 | margin-right: -50vw; 11 | &, .FullApp-content { 12 | background-color: $background; 13 | } 14 | .clickable { 15 | cursor: pointer; 16 | } 17 | .logo-title-column { 18 | display: flex; 19 | align-items: center; 20 | 21 | .logo-accompaniment { 22 | display: inline-block; 23 | 24 | .title { 25 | display: block; 26 | color: $shr-context-dark; 27 | font-size: 1.1rem; 28 | font-weight: 600; 29 | padding-left: 5px; 30 | } 31 | 32 | .login { 33 | display: block; 34 | font-size: 0.8rem; 35 | margin-left: 0.4rem; 36 | } 37 | } 38 | } 39 | .summary-header-column { 40 | padding-left:0; 41 | } 42 | #condition-selection-container { 43 | padding: 5px; 44 | } 45 | } 46 | 47 | .vertical-divider { 48 | border-left: 1px solid $line-gray; 49 | padding: 0 .5rem; 50 | } 51 | 52 | .search-icon { 53 | padding-right: 5px; 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/panels/TargetedDataPanel.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .panel-heading { 4 | text-align: left; 5 | } 6 | 7 | .fitted-panel.minimap-container { 8 | width: auto; 9 | } 10 | 11 | .fitted-panel .minimap { 12 | position: absolute; 13 | left: 0; 14 | margin: 0; 15 | background-color: #ffffff; 16 | border: none; 17 | float: none; 18 | backface-visibility: hidden; 19 | -webkit-backface-visibility: hidden; 20 | } 21 | 22 | .minimap-viewport { 23 | background-color: #555; 24 | opacity: 0.15; 25 | width: 100px !important; 26 | } 27 | 28 | .minimap-children { 29 | display: flex; 30 | margin: 3px 10px; 31 | align-items: center; 32 | justify-content: center; 33 | background-color: #fff; 34 | border-radius: 4px; 35 | border: 1px solid $line-gray; 36 | cursor: pointer; 37 | } 38 | 39 | .minimap-children .minimap-title { 40 | text-align: center; 41 | color: $body-gray; 42 | font-size: $size-xs; 43 | } 44 | 45 | .minimap-title-hidden { 46 | position: absolute; 47 | top: 0; 48 | left: 0; 49 | width: 78px; 50 | font-size: $size-xs; 51 | display: block; 52 | visibility: hidden; 53 | } 54 | 55 | .edit-button { 56 | border-radius: 3px; 57 | border: 1px solid $state !important; 58 | color: $interface-blue-text; 59 | margin-bottom: 5px; 60 | } 61 | 62 | .targeted-data-subpanel-no-minimap { 63 | margin-left: 20px; 64 | margin-right: 0px; 65 | } 66 | -------------------------------------------------------------------------------- /src/patientControl/BaseIndexer.js: -------------------------------------------------------------------------------- 1 | import { Component } from 'react'; 2 | 3 | class BaseIndexer extends Component { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | if (subsection) { 6 | const sectionId = this.getStringForId(section); 7 | const subsectionId = this.getStringForId(subsection); 8 | searchIndex.addSearchableData({ 9 | id: `${sectionId}_${subsectionId}_subsection`, 10 | section, 11 | subsection, 12 | valueTitle: "Subsection", 13 | value: subsection, 14 | onHighlight 15 | }); 16 | } 17 | } 18 | 19 | getStringForId(s) { 20 | try { 21 | return s.toLowerCase().replace(/[.,#!$%&;:{}=\-_`~()]/g,"").replace(/ /g, '_'); 22 | } catch (e) { 23 | console.error(e); 24 | return ''; 25 | } 26 | } 27 | } 28 | 29 | export default BaseIndexer; -------------------------------------------------------------------------------- /src/patientControl/ClusterPointsIndexer.js: -------------------------------------------------------------------------------- 1 | import BaseIndexer from './BaseIndexer'; 2 | 3 | class ClusterPointsIndexer extends BaseIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | super.indexData(section, subsection, data, searchIndex, onHighlight); 6 | } 7 | } 8 | 9 | export default ClusterPointsIndexer; -------------------------------------------------------------------------------- /src/patientControl/DiseaseStatusValuesIndexer.js: -------------------------------------------------------------------------------- 1 | import BaseIndexer from './BaseIndexer'; 2 | 3 | class DiseaseStatusValuesIndexer extends BaseIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | super.indexData(section, subsection, data, searchIndex, onHighlight); 6 | const sectionId = super.getStringForId(section); 7 | data.potentialDiagnosisDates.forEach(item => { 8 | const valueTitleId = super.getStringForId(item.label); 9 | searchIndex.addSearchableData({ 10 | id: `${sectionId}_${valueTitleId}`, 11 | section, 12 | subsection: "", 13 | valueTitle: item.label, 14 | value: item.date, 15 | onHighlight 16 | }); 17 | }); 18 | 19 | data.progressions.forEach(progression => { 20 | const valueTitleId = super.getStringForId(progression.start_time); 21 | searchIndex.addSearchableData({ 22 | id: `${sectionId}_${valueTitleId}`, 23 | section, 24 | subsection: "", 25 | valueTitle: progression.start_time, 26 | value: progression.disease_status_string, 27 | onHighlight 28 | }); 29 | }); 30 | } 31 | } 32 | 33 | export default DiseaseStatusValuesIndexer; -------------------------------------------------------------------------------- /src/patientControl/EventsIndexer.js: -------------------------------------------------------------------------------- 1 | import BaseIndexer from './BaseIndexer'; 2 | 3 | class EventsIndexer extends BaseIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | super.indexData(section, subsection, data, searchIndex, onHighlight); 6 | const sectionId = super.getStringForId(section); 7 | const subsectionId = super.getStringForId(sectionId); 8 | data.forEach(item => { 9 | const value = subsection === "Procedures" ? item.hoverText : `${item.hoverTitle}: ${item.hoverText}`; 10 | searchIndex.addSearchableData({ 11 | id: `${sectionId}_${subsectionId}_${super.getStringForId(value)}`, 12 | section, 13 | subsection, 14 | valueTitle: "", 15 | value, 16 | onHighlight 17 | }); 18 | }); 19 | } 20 | } 21 | 22 | export default EventsIndexer; -------------------------------------------------------------------------------- /src/patientControl/NameValuePairsIndexer.js: -------------------------------------------------------------------------------- 1 | import Lang from 'lodash'; 2 | import BaseIndexer from './BaseIndexer'; 3 | 4 | class NameValuePairsIndexer extends BaseIndexer { 5 | indexData(section, subsection, data, searchIndex, onHighlight) { 6 | super.indexData(section, subsection, data, searchIndex, onHighlight); 7 | const sectionId = super.getStringForId(section); 8 | let value; 9 | data.forEach(obj => { 10 | if (Lang.isObject(obj.value)) { 11 | value = obj.value.value || "Missing Data"; 12 | } else { 13 | value = obj.value || "Missing Data"; 14 | } 15 | searchIndex.addSearchableData({ 16 | id: `${sectionId}_${super.getStringForId(obj.name)}`, 17 | section, 18 | subsection: "", 19 | valueTitle: obj.name, 20 | value, 21 | onHighlight 22 | }); 23 | }); 24 | } 25 | } 26 | 27 | export default NameValuePairsIndexer; -------------------------------------------------------------------------------- /src/patientControl/NotesIndexer.js: -------------------------------------------------------------------------------- 1 | import NoteContentIndexer from './NoteContentIndexer'; 2 | 3 | class NotesIndexer extends NoteContentIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight, onClick) { 5 | const noteSectionId = super.getStringForId(section); 6 | searchIndex.addSearchableData({ 7 | id: noteSectionId, 8 | section, 9 | subsection: '', 10 | valueTitle: 'Section', 11 | value: section, 12 | }); 13 | 14 | searchIndex.addSearchableData({ 15 | id: `${noteSectionId}_signed_notes`, 16 | section, 17 | subsection: 'Signed Notes', 18 | valueTitle: 'Subsection', 19 | value: 'Signed Notes', 20 | }); 21 | 22 | searchIndex.addSearchableData({ 23 | id: `${noteSectionId}_in_progress_notes`, 24 | section, 25 | subsection: 'In Progress Notes', 26 | valueTitle: 'Subsection', 27 | value: 'In Progress Notes', 28 | }); 29 | 30 | data.forEach(note => { 31 | const subsectionId = note.signed ? 'signed_notes' : 'in_progress_notes'; 32 | if (searchIndex.hasDocument(`open_note_${subsectionId}_content_${note.entryInfo.entryId.id}`)) return; 33 | super.indexData(section, subsection, note, searchIndex, onHighlight, onClick); 34 | }); 35 | } 36 | } 37 | 38 | export default NotesIndexer; 39 | -------------------------------------------------------------------------------- /src/patientControl/ReviewOfSystemsValuesIndexer.js: -------------------------------------------------------------------------------- 1 | import BaseIndexer from './BaseIndexer'; 2 | 3 | class ReviewOfSystemsValuesIndexer extends BaseIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | super.indexData(section, subsection, data, searchIndex, onHighlight); 6 | const sectionId = super.getStringForId(section); 7 | 8 | data.forEach((item) => { 9 | const dateId = super.getStringForId(item.date); 10 | searchIndex.addSearchableData({ 11 | id: `${sectionId}_${dateId}`, 12 | section, 13 | subsection: "", 14 | valueTitle: 'Date', 15 | value: item.date, 16 | date: item.date, 17 | onHighlight 18 | }); 19 | 20 | item.questions.forEach((question) => { 21 | const questionId = super.getStringForId(question.name); 22 | searchIndex.addSearchableData({ 23 | id: `${sectionId}_${dateId}_${questionId}`, 24 | section, 25 | subsection: "", 26 | valueTitle: question.name, 27 | value: question.value, 28 | date: item.date, 29 | onHighlight 30 | }); 31 | }); 32 | }); 33 | } 34 | } 35 | 36 | export default ReviewOfSystemsValuesIndexer; -------------------------------------------------------------------------------- /src/patientControl/SearchSuggestion.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .suggestion { 4 | &-item { 5 | display: block; 6 | align-items: center; 7 | padding: 10px; 8 | } 9 | 10 | &-label { 11 | min-width: 80px; 12 | display: block; 13 | font-size: 10px; 14 | width: 80px; 15 | padding: 5px auto; 16 | 17 | & .label-content { 18 | margin: 0; 19 | display: inline-block; 20 | white-space: nowrap; 21 | line-height: 1.2rem; 22 | text-overflow: ellipsis; 23 | } 24 | } 25 | 26 | &-text { 27 | font-size: 14px; 28 | display: block; 29 | white-space: nowrap; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | } 33 | } 34 | 35 | .highlighted-input-value { 36 | font-weight: 600; 37 | } 38 | 39 | .dividing-line:after { 40 | background: #b1b1b1; 41 | width: 1px; 42 | content: ""; 43 | display: inline-block; 44 | top: 0; 45 | bottom: 0; 46 | right: 0; 47 | min-height: 25px; 48 | margin: -5px 10px -5px 10px; 49 | } -------------------------------------------------------------------------------- /src/patientControl/ValueOverTimeIndexer.js: -------------------------------------------------------------------------------- 1 | import BaseIndexer from './BaseIndexer'; 2 | 3 | class ValueOverTimeIndexer extends BaseIndexer { 4 | indexData(section, subsection, data, searchIndex, onHighlight) { 5 | super.indexData(section, subsection, data, searchIndex, onHighlight); 6 | const sectionId = super.getStringForId(section); 7 | const subsectionId = super.getStringForId(subsection); 8 | data.forEach(item => { 9 | const value = item.displayValue || `${item[subsection]} ${item.unit}`; 10 | searchIndex.addSearchableData({ 11 | id: `${sectionId}_${subsectionId}_${super.getStringForId(value)}`, 12 | section, 13 | subsection, 14 | valueTitle: subsection, 15 | value: `${item.start_time}: ${value}` 16 | }); 17 | }); 18 | } 19 | } 20 | 21 | export default ValueOverTimeIndexer; -------------------------------------------------------------------------------- /src/preferences/IPreferenceStore.jsx: -------------------------------------------------------------------------------- 1 | class IPreferenceStore { 2 | setPreference(name, value) { 3 | } 4 | 5 | getPreference(name) { 6 | return null; 7 | } 8 | 9 | removePreference(name) { 10 | } 11 | } 12 | 13 | export default IPreferenceStore; -------------------------------------------------------------------------------- /src/preferences/LocalStoragePreferenceStore.jsx: -------------------------------------------------------------------------------- 1 | import IPreferenceStore from './IPreferenceStore'; 2 | 3 | class LocalStoragePreferenceStore extends IPreferenceStore { 4 | setPreference(name, value) { 5 | try { 6 | localStorage.setItem(name, JSON.stringify(value)); 7 | } catch (e) { 8 | } 9 | } 10 | 11 | getPreference(name) { 12 | try { 13 | return JSON.parse(localStorage.getItem(name)); 14 | } catch (e) { 15 | return null; 16 | } 17 | } 18 | 19 | removePreference(name) { 20 | try { 21 | localStorage.removeItem(name); 22 | } catch (e) { 23 | } 24 | } 25 | } 26 | 27 | export default LocalStoragePreferenceStore; -------------------------------------------------------------------------------- /src/preferences/PreferenceManager.jsx: -------------------------------------------------------------------------------- 1 | import LocalStoragePreferenceStore from './LocalStoragePreferenceStore'; 2 | 3 | class PreferenceManager { 4 | constructor(user) { 5 | this._user = user; 6 | this._preferenceStore = this._getPreferenceStore(); 7 | } 8 | 9 | _getPreferenceStore() { 10 | return new LocalStoragePreferenceStore(); 11 | } 12 | 13 | /* 14 | * name = name of preference to store 15 | * value = value for the named preference item 16 | */ 17 | setPreference(name, value) { 18 | this._preferenceStore.setPreference(name, value); 19 | } 20 | 21 | removePreference(name) { 22 | this._preferenceStore.removePreference(name); 23 | } 24 | 25 | getPreference(name) { 26 | return this._preferenceStore.getPreference(name); 27 | } 28 | } 29 | 30 | export default PreferenceManager; -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import mcodeReducer from './mcode'; 4 | 5 | export default combineReducers({ 6 | mcode: mcodeReducer 7 | }); 8 | -------------------------------------------------------------------------------- /src/shortcuts/NLPHashtag.jsx: -------------------------------------------------------------------------------- 1 | import EntryShortcut from './EntryShortcut'; 2 | 3 | export default class NLPHashtag extends EntryShortcut { 4 | constructor(onUpdate, metadata, patient, shortcutData) { 5 | super(metadata); 6 | this.nlpTemplate = this.metadata.nlpTemplate; 7 | this.isSetByLabel = {}; 8 | this.onUpdate = onUpdate; 9 | this.patient = patient; 10 | this._initializeValueObject(metadata, patient, shortcutData); 11 | this._initializeValueObjectAttributes(metadata); 12 | this.setAttributeValue = this.setAttributeValue.bind(this); 13 | } 14 | 15 | getAttributeIsSetByLabel(name) { 16 | return this.isSetByLabel[name]; 17 | } 18 | 19 | setAttributeIsSetByLabel(name, val) { 20 | this.isSetByLabel[name] = val; 21 | } 22 | 23 | get isComplete() { 24 | return this.hasParentContext() && this.hasChildren(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import { routerMiddleware } from 'react-router-redux'; 3 | import thunk from 'redux-thunk'; 4 | import { createHashHistory as createHistory } from 'history'; 5 | import rootReducer from '../reducers'; 6 | 7 | export const history = createHistory(); 8 | 9 | const initialState = {}; 10 | const enhancers = []; 11 | const middleware = [ 12 | thunk, 13 | routerMiddleware(history) 14 | ]; 15 | 16 | if (process.env.NODE_ENV === 'development') { 17 | const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__; 18 | 19 | if (typeof devToolsExtension === 'function') { 20 | enhancers.push(devToolsExtension()); 21 | } 22 | } 23 | 24 | const composedEnhancers = compose( 25 | applyMiddleware(...middleware), 26 | ...enhancers 27 | ); 28 | 29 | const store = createStore( 30 | rootReducer, 31 | initialState, 32 | composedEnhancers 33 | ); 34 | 35 | export default store; 36 | -------------------------------------------------------------------------------- /src/styles/SlimApp.css: -------------------------------------------------------------------------------- 1 | /* Restricts app to max fixed width for very wide screens. */ 2 | .SlimApp-content { 3 | background-color: white; 4 | font-size:0.93rem; 5 | /*64 px is the height of the nav bar*/ 6 | height: calc(100vh - 64px); 7 | padding: 0 0; 8 | } 9 | 10 | .SlimApp-content .row{ 11 | margin: 0!important; 12 | } 13 | 14 | .SlimApp-content .divider { 15 | margin: 10px 0 20px 0px !important; 16 | clear: both; 17 | } 18 | 19 | .SlimApp-content h1 { 20 | margin: 10px 0; 21 | font-size: 1.25rem; 22 | font-family: "Open Sans", Arial, sans-serif; 23 | } 24 | 25 | .SlimApp-content h2 { 26 | margin: 0px; 27 | font-size: 0.9rem; 28 | font-weight: lighter; 29 | font-family: "Open Sans", Arial, sans-serif; 30 | } 31 | 32 | .SlimApp-content h3 { 33 | margin: 0px; 34 | font-size: 0.8rem; 35 | font-weight: lighter; 36 | } 37 | 38 | .SlimApp-content h4 { 39 | margin: 10px 0; 40 | font-size: 0.9rem; 41 | font-weight: 600; 42 | } 43 | 44 | .SlimApp-content ::-webkit-scrollbar-track { 45 | background-color: #FFFFFF; 46 | } 47 | 48 | .SlimApp-content .no-padding { 49 | padding: 0!important; 50 | } 51 | 52 | .SlimApp-content .header-spacing { 53 | margin-top: 25px; 54 | } 55 | 56 | .SlimApp-content a { 57 | color: #297DA2; 58 | } 59 | 60 | 61 | .SlimApp-content .data-element-description { 62 | margin-top: 15px; 63 | font-style: italic; 64 | font-size: 0.9em; 65 | } -------------------------------------------------------------------------------- /src/styles/mixins/_colors.scss: -------------------------------------------------------------------------------- 1 | @import '../variables'; 2 | 3 | $colorMap: ( 4 | color-1: $icons-purple, 5 | color-2: $icons-green, 6 | color-3: $icons-blue, 7 | color-4: $icons-red, 8 | color-5: $icons-teal 9 | ); 10 | 11 | @mixin index-colors($attribute: 'color', $prefix: '-', $opacity: 1) { 12 | @each $color-index, $color in $colorMap { 13 | &#{$prefix}#{$color-index} { 14 | #{$attribute}: rgba($color, $opacity); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/styles/mixins/_media-queries.scss: -------------------------------------------------------------------------------- 1 | // Breakpoints 2 | $breakpoint-tablet: 576px; 3 | $breakpoint-desktop: 768px; 4 | 5 | // Media queries 6 | @mixin respond-to($media) { 7 | @if $media==mobile { 8 | @media only screen and (max-width: $breakpoint-tablet) { @content; } 9 | } 10 | @else if $media==tablet { 11 | @media only screen and (min-width: $breakpoint-tablet + 1) and (max-width: $breakpoint-desktop) { @content; } 12 | } 13 | @else if $media==desktop { 14 | @media only screen and (min-width: $breakpoint-desktop + 1) { @content; } 15 | } 16 | @else { 17 | @media only screen and (max-width: $media) { @content; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/summary/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /MedicationRangeChartVisualizer.css 3 | /ExpandedTableVisualizer.css 4 | /BandedLineChartVisualizer.css 5 | /SummaryHeader.css 6 | -------------------------------------------------------------------------------- /src/summary/BandedLineChartVisualizer.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .line-chart-subsection { 4 | .subsection-heading { 5 | h2 { 6 | margin: 10px 0; 7 | 8 | .subsection-name { 9 | font-weight: $weight-regular; 10 | font-size: $size-m; 11 | } 12 | .subsection-icons { 13 | margin-left: 5px; 14 | 15 | .small-btn { 16 | min-width: 0 !important; 17 | min-height: 0 !important; 18 | } 19 | } 20 | } 21 | } 22 | 23 | .no-entries { 24 | padding: 20px 0px 10px 0px; 25 | font-size: $size-l; 26 | font-weight: $weight-regular; 27 | color: $state; 28 | } 29 | 30 | .recharts-responsive-container { 31 | .recharts-tooltip-wrapper { 32 | font-size: $size-m; 33 | } 34 | 35 | .recharts-text { 36 | font-size: $size-s; 37 | } 38 | 39 | .hide-line { 40 | .recharts-curve { 41 | stroke: none; 42 | 43 | &.recharts-tooltip-cursor { 44 | stroke: #CCCCCC; 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/summary/ClinicalEventSelection.css: -------------------------------------------------------------------------------- 1 | .clinical-event-selection .clinical-event-select { 2 | width: 90%; 3 | height: 30px; 4 | font-size: 0.8em; 5 | background-color: #fafafa; 6 | border-radius: 2px; 7 | padding: 5px 2px 2px 20px; 8 | border: none; 9 | -webkit-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 10 | -moz-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 11 | box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 12 | } 13 | 14 | .clinical-event-select .MuiSelect-select-111:focus { 15 | background: inherit; 16 | } 17 | 18 | 19 | li.clinical-event-item { 20 | font-family: "Open Sans", Roboto, Arial, sans-serif; 21 | color: #444; 22 | height: 10px; 23 | margin: 5px 0; 24 | font-size: 0.9em; 25 | } 26 | 27 | li.clinical-event-item:hover { 28 | background-color: #1384b5; 29 | color: #fff; 30 | } -------------------------------------------------------------------------------- /src/summary/CompassAppSummaryMetadata.jsx: -------------------------------------------------------------------------------- 1 | import SummaryMetadata from './SummaryMetadata'; 2 | import DefaultCompassAppMetadata from './metadata/DefaultCompassAppMetadata'; 3 | import McodeMetadata from './metadata/McodeMetadata'; 4 | import FluxCancerCondition from '../model/fluxWrappers/onco/core/FluxCancerCondition'; 5 | import FunctionMatcher from './matchers/FunctionMatcher'; 6 | import AlwaysMatcher from './matchers/AlwaysMatcher'; 7 | 8 | export default class CompassAppSummaryMetadata extends SummaryMetadata { 9 | constructor(setForceRefresh) { 10 | super(); 11 | this.setForceRefresh = setForceRefresh; 12 | 13 | this.hardCodedMetadata = [ 14 | { "enabled": true, "type": FunctionMatcher, "matchFunction": (condition) => condition instanceof FluxCancerCondition, "metadata": McodeMetadata }, 15 | { "enabled": true, "type": AlwaysMatcher, "metadata": DefaultCompassAppMetadata }, 16 | ]; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/summary/ConditionSelection.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | #condition-selection { 4 | .condition-select { 5 | width: 95%; 6 | height: 30px; 7 | padding-top: 2px; 8 | opacity: 0.9; 9 | font-size: $size-s; 10 | //background-color: $contrast-gray; 11 | border: none; 12 | //-webkit-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 13 | //-moz-box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 14 | //box-shadow: 0px 1px 5px 0px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 3px 1px -2px rgba(0, 0, 0, 0.12); 15 | &:after { 16 | background-color: $shr-context-dark; 17 | } 18 | } 19 | li.condition-item { 20 | font-family: "Open Sans", Roboto, Arial, sans-serif; 21 | color: #444; 22 | height: 10px; 23 | margin: 5px 0; 24 | font-size: 0.9em; 25 | } 26 | .condition-select-menu { 27 | padding-left: 0px; 28 | color: $body-black; 29 | size: $size-s; 30 | } 31 | .label { 32 | font-size: $size-s; 33 | color: $body-gray; 34 | margin: 0px; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/summary/FormatTabularListVisualizer.css: -------------------------------------------------------------------------------- 1 | stopped-cell { 2 | padding: 8px; 3 | border: 0; 4 | color: #bd4e3a; 5 | font-family: "Open Sans", Arial, sans-serif; 6 | } -------------------------------------------------------------------------------- /src/summary/Pilot2MvpAppSummaryMetadata.jsx: -------------------------------------------------------------------------------- 1 | import SummaryMetadata from './SummaryMetadata'; 2 | import DefaultPilot2MvpAppMetadata from './metadata/DefaultPilot2MvpAppMetadata'; 3 | import Pilot2MvpMetadata from './metadata/Pilot2MvpMetadata'; 4 | import FluxCancerCondition from '../model/fluxWrappers/onco/core/FluxCancerCondition'; 5 | import FunctionMatcher from './matchers/FunctionMatcher'; 6 | import AlwaysMatcher from './matchers/AlwaysMatcher'; 7 | 8 | export default class Pilot2MvpAppSummaryMetadata extends SummaryMetadata { 9 | constructor(setForceRefresh) { 10 | super(); 11 | this.setForceRefresh = setForceRefresh; 12 | 13 | this.hardCodedMetadata = [ 14 | { "enabled": true, "type": FunctionMatcher, "matchFunction": (condition) => condition instanceof FluxCancerCondition, "metadata": Pilot2MvpMetadata }, 15 | { "enabled": true, "type": AlwaysMatcher, "metadata": DefaultPilot2MvpAppMetadata }, 16 | ]; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/summary/ProgressionLineChartVisualizer.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .progression-line-chart-subsection { 4 | padding: 10px 0; 5 | 6 | .sub-section-heading h2.sub-section-name { 7 | margin: 10px 0; 8 | font-weight: $weight-regular; 9 | } 10 | 11 | .recharts-tooltip-wrapper { 12 | font-size: $size-m; 13 | } 14 | 15 | .recharts-text { 16 | font-size: $size-s; 17 | } 18 | 19 | .disease-status-tooltip { 20 | padding: 10px; 21 | background-color: rgb(255, 255, 255); 22 | border: 1px solid rgb(204, 204, 204); 23 | max-width: 200px; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/summary/RangeChart.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FluxNotes/flux/dcfa6d78442cb9ca51ec627eb5cd193bd76a38db/src/summary/RangeChart.css -------------------------------------------------------------------------------- /src/summary/ScatterPlotVisualizer.css: -------------------------------------------------------------------------------- 1 | @import 'https://code.highcharts.com/css/highcharts.css'; 2 | 3 | .highcharts-title *{ 4 | color: #0067B0; 5 | fill: #0067b0; 6 | font-weight: bold; 7 | } 8 | 9 | #table-loading-animation-container { 10 | display:flex; 11 | justify-content: center; 12 | width: 100%; 13 | overflow: hidden; 14 | padding: 20px 0px; 15 | } 16 | 17 | .highcharts-axis-title *{ 18 | left: 0px; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/summary/TargetedDataSubpanel.css: -------------------------------------------------------------------------------- 1 | #condition-summary-section { 2 | text-align: left; 3 | overflow-x: hidden; 4 | } 5 | 6 | .tabs-container div .tab{ 7 | color: black !important; 8 | } 9 | 10 | .table-list h2 { 11 | color: #333; 12 | } 13 | 14 | .table-list { 15 | margin-top: 20px; 16 | } 17 | 18 | /*table.existing-notes td {*/ 19 | /*padding: 20px 10px;*/ 20 | /*}*/ 21 | 22 | /*td.existing-note-metadata {*/ 23 | /*color: #333333;*/ 24 | /*}*/ 25 | 26 | /*td.existing-note-date {*/ 27 | /*color: #999;*/ 28 | /*}*/ 29 | 30 | /*td span#existing-note-subject {*/ 31 | /*font-weight: bold;*/ 32 | /*font-size: 1rem;*/ 33 | /*}*/ 34 | 35 | /*Explicitly add in top and bottom border for last cell in row because it is removed in the Data Summary Table css*/ 36 | tr.existing-note-entry td:last-child { 37 | border-top: 1px solid #ccc; 38 | border-bottom: 1px solid #ccc; 39 | } 40 | 41 | #condition-summary-section h2.section-header{ 42 | /*font-weight: bold;*/ 43 | /*color: #333;*/ 44 | } 45 | 46 | .icons { 47 | float: right; 48 | padding: 10px; 49 | } 50 | -------------------------------------------------------------------------------- /src/summary/activeTreatmentSummary/ActiveTreatmentSummaryObjectFactory.js: -------------------------------------------------------------------------------- 1 | 2 | import CancerDisorderPresentActiveTreatmentSummary from './CancerDisorderPresentActiveTreatmentSummary'; 3 | import FluxCancerCondition from '../../model/fluxWrappers/onco/core/FluxCancerCondition'; 4 | /* 5 | * ActiveTreatmentSummaryFactory class returns instances of Flux model classes 6 | * Default case will return SHR model classes if no Flux wrapper class is found 7 | */ 8 | export default class ActiveTreatmentSummaryObjectFactory { 9 | static createInstance(patient, currentConditionEntry) { 10 | // Can't do an instance of with a switch 11 | if (currentConditionEntry instanceof FluxCancerCondition) return new CancerDisorderPresentActiveTreatmentSummary(); 12 | else return null; 13 | } 14 | } -------------------------------------------------------------------------------- /src/summary/activeTreatmentSummary/IActiveTreatmentSummary.js: -------------------------------------------------------------------------------- 1 | class IActiveTreatmentSummary { 2 | getActiveTreatmentSummary(patient, currentConditionEntry) { 3 | return null; 4 | } 5 | } 6 | 7 | export default IActiveTreatmentSummary; -------------------------------------------------------------------------------- /src/summary/matchers/AlwaysMatcher.js: -------------------------------------------------------------------------------- 1 | import Matcher from './Matcher'; 2 | 3 | class AlwaysMatcher extends Matcher { 4 | match = (condition, roleType, role, specialty) => { 5 | return true; 6 | } 7 | } 8 | 9 | export default AlwaysMatcher; -------------------------------------------------------------------------------- /src/summary/matchers/FunctionMatcher.js: -------------------------------------------------------------------------------- 1 | import Matcher from './Matcher'; 2 | 3 | class FunctionMatcher extends Matcher { 4 | match = (condition, roleType, role, specialty) => { 5 | return this._metadata.matchFunction(condition, roleType, role, specialty); 6 | } 7 | } 8 | 9 | export default FunctionMatcher; -------------------------------------------------------------------------------- /src/summary/matchers/Matcher.js: -------------------------------------------------------------------------------- 1 | class Matcher { 2 | constructor(metadata) { 3 | this._metadata = metadata; 4 | } 5 | 6 | match = (condition, roleType, role, specialty) => { 7 | return false; 8 | } 9 | } 10 | 11 | export default Matcher; -------------------------------------------------------------------------------- /src/summary/matchers/StringMatcher.js: -------------------------------------------------------------------------------- 1 | import Matcher from './Matcher'; 2 | import Lang from 'lodash'; 3 | 4 | class StringMatcher extends Matcher { 5 | // returns an array of strings which are keys to find the metadata for summary in priority order (1st one should be used first). List should always end with DefautMetadata 6 | buildPrioritizedMetadataKeyList = (condition, roleType, role, specialty) => { 7 | if (Lang.isNull(condition)) return [ "default" ]; 8 | const codeSystem = condition.codeSystem; 9 | const code = condition.code; 10 | const conditionType = `${codeSystem}/${code}`; 11 | const userType = `${roleType}/${role}/${specialty}`; 12 | return [ userType + "/" + conditionType, conditionType, "default" ]; 13 | } 14 | 15 | match = (condition, roleType, role, specialty) => { 16 | const prioritizedKeyList = this.buildPrioritizedMetadataKeyList(condition, roleType, role, specialty); 17 | const numKeys = prioritizedKeyList.length; 18 | let keyIndex = 0; 19 | while (keyIndex < numKeys) { 20 | if (prioritizedKeyList[keyIndex] === this._metadata.matchString) return true; 21 | keyIndex++; 22 | } 23 | return false; 24 | } 25 | } 26 | 27 | export default StringMatcher; -------------------------------------------------------------------------------- /src/summary/metadata/DetailedTreatmentOptionsSection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | 3 | export default class DetailedTreatmentOptionsSection extends MetadataSection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Treatment Options", 7 | shortName: "Treatments", 8 | type: "TreatmentOptions", 9 | data: [] 10 | }; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/summary/metadata/HeartRateSubsection.jsx: -------------------------------------------------------------------------------- 1 | import VitalsSubsection from './VitalsSubsection'; 2 | 3 | export default class HeartRateSubsection extends VitalsSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Heart Rate", 7 | code: "8867-4", 8 | itemsFunction: this.getVitalsForSubsection, 9 | displayChartLine: false, 10 | 11 | // Source: https://www.medicalnewstoday.com/articles/235710.php 12 | bands: [ 13 | { 14 | low: 40, 15 | high: 50, 16 | assessment: 'bad' 17 | }, 18 | 19 | { 20 | low: 50, 21 | high: 100, 22 | assessment: 'good' 23 | }, 24 | { 25 | low: 100, 26 | high: 200, 27 | assessment: 'bad' 28 | } 29 | ] 30 | }; 31 | } 32 | } -------------------------------------------------------------------------------- /src/summary/metadata/HemoglobinSubsection.jsx: -------------------------------------------------------------------------------- 1 | import LabTestSubsection from "./LabTestSubsection"; 2 | 3 | export default class HemoglobinSubsection extends LabTestSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Hemoglobin", 7 | code: "C0019046", 8 | itemsFunction: this.getTestsForSubSection, 9 | displayChartLine: false, 10 | 11 | // Source: https://www.emedicinehealth.com/hemoglobin_levels/page2_em.htm 12 | // Source: https://www.quora.com/What-is-the-percentage-of-haemoglobin-in-blood 13 | bands: [ 14 | { 15 | low: 0, 16 | high: 12, 17 | assessment: 'bad' 18 | }, 19 | 20 | { 21 | low: 12, 22 | high: 16, 23 | assessment: 'good' 24 | }, 25 | { 26 | low: 16, 27 | high: 20, 28 | assessment: 'bad' 29 | } 30 | ] 31 | }; 32 | } 33 | } -------------------------------------------------------------------------------- /src/summary/metadata/LabTestSubsection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import _ from 'lodash'; 3 | 4 | export default class LabTestSubsection extends MetadataSection { 5 | getTestsForSubSection = (patient, currentConditionEntry, subsection) => { 6 | if (_.isNull(patient) || _.isNull(currentConditionEntry)) return []; 7 | const labResults = currentConditionEntry.getLabResultsChronologicalOrder(); 8 | 9 | return labResults.filter(lab => lab.codeableConceptCode === subsection.code).map((lab) => { 10 | const processedLab = { 11 | start_time: lab.relevantTime, 12 | unit: lab.quantity.unit 13 | }; 14 | 15 | processedLab[subsection.name] = lab.quantity.number; 16 | 17 | return processedLab; 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/summary/metadata/McodeMetadata.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import GeneralCancerSummarySection from './GeneralCancerSummarySection'; 3 | import DetailedTreatmentOptionsSection from './DetailedTreatmentOptionsSection'; 4 | import DiseaseStatusSection from './DiseaseStatusSection'; 5 | import ProceduresSection from './ProceduresSection'; 6 | import MedicationsSection from './MedicationsSection'; 7 | import ActiveConditionsSection from "./ActiveConditionsSection"; 8 | 9 | export default class McodeMetadata extends MetadataSection { 10 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 11 | return { // sarcoma 12 | sections: this.buildMetadataSections(preferencesManager, patient, condition, roleType, role, specialty, 13 | GeneralCancerSummarySection, 14 | ActiveConditionsSection, 15 | MedicationsSection, 16 | ProceduresSection, 17 | DiseaseStatusSection, 18 | DetailedTreatmentOptionsSection 19 | ) 20 | }; 21 | } 22 | } -------------------------------------------------------------------------------- /src/summary/metadata/NeutrophilCountSubsection.jsx: -------------------------------------------------------------------------------- 1 | import LabTestSubsection from "./LabTestSubsection"; 2 | 3 | export default class NeutrophilCountSubsection extends LabTestSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Neutrophil count", 7 | code: "C0027950", 8 | itemsFunction: this.getTestsForSubSection, 9 | displayChartLine: false, 10 | 11 | // Source: https://www.healthline.com/health/neutrophils#anc 12 | // Source: https://evs.nci.nih.gov/ftp1/CTCAE/CTCAE_4.03_2010-06-14_QuickReference_8.5x11.pdf page 42 13 | bands: [ 14 | { 15 | low: 0, 16 | high: 1, 17 | assessment: 'bad' 18 | }, 19 | { 20 | low: 1, 21 | high: 8, 22 | assessment: 'good' 23 | }, 24 | { 25 | low: 8, 26 | // Only draws if an element is captured in this range 27 | high: 'max', 28 | assessment: 'bad' 29 | } 30 | ] 31 | }; 32 | } 33 | } -------------------------------------------------------------------------------- /src/summary/metadata/Pilot2MvpMetadata.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from './MetadataSection'; 2 | import SarcomaSummarySection from './SarcomaSummarySection'; 3 | import TimelineSection from './TimelineSection'; 4 | 5 | export default class Pilot2MvpMetadata extends MetadataSection { 6 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 7 | return { 8 | sections: this.buildMetadataSections(preferencesManager, patient, condition, roleType, role, specialty, 9 | SarcomaSummarySection, 10 | TimelineSection 11 | ) 12 | }; 13 | } 14 | } -------------------------------------------------------------------------------- /src/summary/metadata/PlateletSubsection.jsx: -------------------------------------------------------------------------------- 1 | import LabTestSubsection from "./LabTestSubsection"; 2 | 3 | export default class PlateletSubsection extends LabTestSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Platelet count", 7 | code: "C0005821", 8 | itemsFunction: this.getTestsForSubSection, 9 | displayChartLine: false, 10 | 11 | bands: [ 12 | { 13 | low: 0, 14 | high: 150, 15 | assessment: 'bad' 16 | }, 17 | 18 | { 19 | low: 150, 20 | high: 450, 21 | assessment: 'good' 22 | }, 23 | { 24 | low: 450, 25 | high: 'max', 26 | assessment: 'bad' 27 | } 28 | ] 29 | }; 30 | } 31 | } -------------------------------------------------------------------------------- /src/summary/metadata/RecentLabResultsSubsection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from './MetadataSection'; 2 | import _ from 'lodash'; 3 | import moment from 'moment'; 4 | 5 | export default class RecentLabResultsSubsection extends MetadataSection { 6 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 7 | return { 8 | name: "Recent Labs (last 6 Months)", 9 | itemsFunction: this.getItemListForLabResults 10 | }; 11 | } 12 | 13 | getItemListForLabResults = (patient, currentConditionEntry) => { 14 | if (_.isNull(patient) || _.isNull(currentConditionEntry)) return []; 15 | 16 | // set a const for the number of months that dictates most recent lab results 17 | const numberOfMonths = 6; 18 | const labResultsInOrder = currentConditionEntry.getLabResultsChronologicalOrder(moment().subtract(numberOfMonths, 'months')); 19 | 20 | return labResultsInOrder.map((l, i) => { 21 | const value = `${l.quantity.number} ${l.quantity.unit} (${l.relevantTime})`; 22 | const name = `${l.name}`; 23 | return { name: name, 24 | value: {value}, 25 | shortcut: null 26 | }; 27 | }); 28 | } 29 | } -------------------------------------------------------------------------------- /src/summary/metadata/SarcomaCompassAppMetadata.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import SarcomaSummarySection from './SarcomaSummarySection'; 3 | import DetailedTreatmentOptionsSection from './DetailedTreatmentOptionsSection'; 4 | 5 | export default class SarcomaCompassAppMetadata extends MetadataSection { 6 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 7 | return { // sarcoma 8 | sections: this.buildMetadataSections(preferencesManager, patient, condition, roleType, role, specialty, 9 | SarcomaSummarySection, 10 | DetailedTreatmentOptionsSection 11 | ) 12 | }; 13 | } 14 | } -------------------------------------------------------------------------------- /src/summary/metadata/SarcomaLabsSection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import WhiteBloodCellCountSubsection from './WhiteBloodCellCountSubsection'; 3 | import NeutrophilCountSubsection from "./NeutrophilCountSubsection"; 4 | import HemoglobinSubsection from "./HemoglobinSubsection"; 5 | import PlateletSubsection from "./PlateletSubsection"; 6 | 7 | export default class SarcomaLabsSection extends MetadataSection { 8 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 9 | return { 10 | name: "Labs", 11 | shortName: "Labs", 12 | subsectionLabel: "Lab Name", 13 | clinicalEvents: ["pre-encounter"], 14 | type: "ValueOverTime", 15 | data: [ 16 | WhiteBloodCellCountSubsection, 17 | NeutrophilCountSubsection, 18 | HemoglobinSubsection, 19 | PlateletSubsection, 20 | ] 21 | }; 22 | } 23 | } -------------------------------------------------------------------------------- /src/summary/metadata/TemperatureSubsection.jsx: -------------------------------------------------------------------------------- 1 | import VitalsSubsection from './VitalsSubsection'; 2 | 3 | export default class TemperatureSubsection extends VitalsSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Temperature", 7 | code: "8310-5", 8 | itemsFunction: this.getVitalsForSubsection, 9 | displayChartLine: false, 10 | 11 | // Source: https://medlineplus.gov/ency/article/001982.htm#start 12 | bands: [ 13 | { 14 | low: 85, 15 | high: 97, 16 | assessment: 'bad' 17 | }, 18 | 19 | { 20 | low: 97, 21 | high: 99, 22 | assessment: 'good' 23 | }, 24 | { 25 | low: 99, 26 | high: 105, 27 | assessment: 'bad' 28 | } 29 | ] 30 | }; 31 | } 32 | } -------------------------------------------------------------------------------- /src/summary/metadata/VitalsSection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import BloodPressureSubsection from './BloodPressureSubsection'; 3 | import TemperatureSubsection from './TemperatureSubsection'; 4 | import WeightSubsection from './WeightSubsection'; 5 | import HeartRateSubsection from './HeartRateSubsection'; 6 | 7 | export default class VitalsSection extends MetadataSection { 8 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 9 | return { 10 | name: "Vitals", 11 | shortName: "Vitals", 12 | subsectionLabel: "Vital Name", 13 | clinicalEvents: ["pre-encounter"], 14 | type: "ValueOverTime", 15 | data: [ 16 | BloodPressureSubsection, 17 | TemperatureSubsection, 18 | WeightSubsection, 19 | HeartRateSubsection 20 | ], 21 | }; 22 | } 23 | } -------------------------------------------------------------------------------- /src/summary/metadata/VitalsSubsection.jsx: -------------------------------------------------------------------------------- 1 | import MetadataSection from "./MetadataSection"; 2 | import Lang from 'lodash'; 3 | 4 | export default class VitalsSubsection extends MetadataSection { 5 | getVitalsForSubsection = (patient, currentConditionEntry, subsection) => { 6 | if (Lang.isNull(patient) || Lang.isNull(currentConditionEntry)) return []; 7 | return patient.getVitalByCode(subsection.code) 8 | .map(v => { 9 | const processedVital = {}; 10 | 11 | processedVital["start_time"] = v.relevantTime; 12 | processedVital[subsection.name] = v.value; 13 | processedVital["unit"] = v.units; 14 | 15 | return processedVital; 16 | }); 17 | } 18 | } -------------------------------------------------------------------------------- /src/summary/metadata/WeightSubsection.jsx: -------------------------------------------------------------------------------- 1 | import VitalsSubsection from './VitalsSubsection'; 2 | 3 | export default class WeightSubsection extends VitalsSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "Weight", 7 | code: "29463-7", 8 | itemsFunction: this.getVitalsForSubsection, 9 | displayChartLine: false, 10 | 11 | // Source: https://www.healthline.com/health/mens-health/average-weight-for-men 12 | bands: [ 13 | { 14 | low: 120, 15 | high: 135, 16 | assessment: 'bad' 17 | }, 18 | 19 | { 20 | low: 135, 21 | high: 180, 22 | assessment: 'good' 23 | }, 24 | { 25 | low: 180, 26 | high: 250, 27 | assessment: 'bad' 28 | } 29 | ] 30 | }; 31 | } 32 | } -------------------------------------------------------------------------------- /src/summary/metadata/WhiteBloodCellCountSubsection.jsx: -------------------------------------------------------------------------------- 1 | import LabTestSubsection from "./LabTestSubsection"; 2 | 3 | export default class WhiteBloodCellCountSubsection extends LabTestSubsection { 4 | getMetadata(preferencesManager, patient, condition, roleType, role, specialty) { 5 | return { 6 | name: "White blood cell count", 7 | code: "C0023508", 8 | itemsFunction: this.getTestsForSubSection, 9 | displayChartLine: false, 10 | 11 | // Source: https://www.cancer.org/treatment/understanding-your-diagnosis/tests/understanding-your-lab-test-results.html 12 | // Source: https://www.mayoclinic.org/symptoms/low-white-blood-cell-count/basics/definition/sym-20050615 13 | bands: [ 14 | { low: 0, high: 3, assessment: 'bad' }, 15 | { low: 3, high: 10, assessment: 'good' }, 16 | { low: 10, high: 'max', assessment: 'bad' } 17 | ] 18 | }; 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /src/templates/TemplateOptionPreviewButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import RemoveRedEye from 'material-ui-icons/RemoveRedEye'; 3 | import "./TemplateOptionPreviewButton.css"; 4 | 5 | export default class TemplateOptionPreviewButton extends Component { 6 | handleClick = (e) => { 7 | // stops note from opening and instead only shows note preview popover 8 | e.stopPropagation(); 9 | this.props.onClick(); 10 | } 11 | 12 | render() { 13 | return ( 14 |
    15 | 16 |
    17 | ); 18 | } 19 | } -------------------------------------------------------------------------------- /src/templates/TemplateOptionPreviewButton.scss: -------------------------------------------------------------------------------- 1 | @import '../styles/variables'; 2 | 3 | .template-preview-button { 4 | position: absolute; 5 | bottom: 0; 6 | right: 0; 7 | display: flex; 8 | padding: 2px 5px; 9 | border: 1px $line-gray solid; 10 | border-top-left-radius: 5px; 11 | border-bottom: 0px; 12 | border-right: 0px; 13 | svg > * { 14 | fill: $icons-gray; 15 | } 16 | &:hover { 17 | cursor: pointer; 18 | background-color: $interface-blue; 19 | svg > * { 20 | fill: $background; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/timeline/HoverItem.css: -------------------------------------------------------------------------------- 1 | .hover-item { 2 | position: fixed; 3 | background-color: #fff; 4 | border: 1px solid #aaa; 5 | padding: 10px; 6 | min-width: 150px; 7 | z-index: 100; 8 | } 9 | 10 | .hover-item p { 11 | margin: 0; 12 | margin-top: 5px; 13 | padding: 0; 14 | font-size: 14px; 15 | } 16 | -------------------------------------------------------------------------------- /src/timeline/HoverItem.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import Paper from 'material-ui/Paper'; 4 | import './HoverItem.css'; 5 | 6 | class HoverItem extends Component { 7 | render() { 8 | const style = this.props.style; 9 | return ( 10 | 11 |

    {this.props.title}

    12 |

    {this.props.text}

    13 |
    14 | ); 15 | } 16 | } 17 | 18 | HoverItem.propTypes = { 19 | title: PropTypes.string, 20 | text: PropTypes.string 21 | }; 22 | 23 | export default HoverItem; 24 | -------------------------------------------------------------------------------- /src/timeline/Item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | // Item provides custom rendering of the the content of a timeline item. 5 | // Items may have any combination of: 6 | // icon - a font-awesome icon to display before the title 7 | // title - a title to display 8 | // details - additional information to display next to the title 9 | function Item({ item }) { 10 | let icon = null; 11 | let details = null; 12 | 13 | if (item.icon) { 14 | icon = ( 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | if (item.details) { 22 | details = ( 23 | 24 |   |   {item.details} 25 | 26 | ); 27 | } 28 | 29 | const itemId = `timeline-item-${item.id}`; 30 | 31 | return ( 32 |
    33 | {icon} 34 | {item.title} 35 | {details} 36 |
    37 | ); 38 | }; 39 | 40 | Item.propTypes = { 41 | item: PropTypes.object.isRequired 42 | }; 43 | 44 | export default Item; -------------------------------------------------------------------------------- /src/timeline/TimelineLegend.css: -------------------------------------------------------------------------------- 1 | .legend { 2 | margin-top: 10px; 3 | font-size: 14px; 4 | } 5 | 6 | .legend-icon { 7 | text-align: center; 8 | min-width: 30px; 9 | } 10 | -------------------------------------------------------------------------------- /src/timeline/TimelineLegend.jsx: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import './TimelineLegend.css'; 4 | 5 | class Legend extends Component { 6 | render() { 7 | return ( 8 |
    9 | {this.props.items.map((item, i) => { 10 | return ( 11 |
    :  {item.description}
    12 | ); 13 | })} 14 |
    15 | ); 16 | } 17 | } 18 | 19 | Legend.propTypes = { 20 | items: PropTypes.arrayOf(PropTypes.shape({ 21 | icon: PropTypes.string, 22 | description: PropTypes.string 23 | })), 24 | }; 25 | 26 | export default Legend; -------------------------------------------------------------------------------- /src/timeline/util.js: -------------------------------------------------------------------------------- 1 | // utils/supportsSticky 2 | const supportsSticky = (() => { 3 | const node = document.createElement('div'); 4 | node.style.position = 'sticky'; 5 | return node.style.position === 'sticky'; 6 | })(); 7 | 8 | export default supportsSticky; --------------------------------------------------------------------------------