diff --git a/backend/pkg/web/handler/dashboard/default.json b/backend/pkg/web/handler/dashboard/default.json index 0f4df93c..745b66b8 100644 --- a/backend/pkg/web/handler/dashboard/default.json +++ b/backend/pkg/web/handler/dashboard/default.json @@ -5,124 +5,29 @@ "description": "An example dashboard to show-off the power of Fasten widgets", "widgets": [ { - "title_text": "Diabetes Tracking", + "title_text": "Records Summary", "description_text": "Track key metrics for your chronic disease (eg. Diabetes). The data within this widget is not reflective of your health record, and is only present for demonstrational purposes.", "x": 0, "y": 0, "width": 8, - "height": 5, - "item_type": "complex-line-widget" - }, - { - "title_text": "Weight", - "description_text": "", - "x": 8, - "y": 0, - "width": 2, - "height": 2, - "item_type": "simple-line-chart-widget", - "queries": [{ - "q": { - "select": [ - "valueQuantity.value as data", - "valueQuantity.unit as unit", - "(effectiveDateTime | issued).first() as label" - ], - "from": "Observation", - "where": { - "code": "http://loinc.org|29463-7,http://loinc.org|3141-9,http://snomed.info/sct|27113001" - } - } - }], - "parsing": { - "xAxisKey": "label", - "yAxisKey": "data" - } - }, - { - "title_text": "Height", - "description_text": "", - "x": 10, - "y": 0, - "width": 2, - "height": 2, - "item_type": "simple-line-chart-widget", - "queries": [{ - "q": { - "select": [ - "valueQuantity.value as data", - "valueQuantity.unit as unit", - "(effectiveDateTime | issued).first() as label" - ], - "from": "Observation", - "where": { - "code": "http://loinc.org|8302-2" - } - } - }], - "parsing": { - "xAxisKey": "label", - "yAxisKey": "data" - } - }, - { - "title_text": "Blood Pressure", - "description_text": "How much pressure your blood is exerting against your artery walls when the heart beats", - "x": 8, - "y": 2, - "width": 4, - "height": 3, - "item_type": "grouped-bar-chart-widget", - "queries": [ - { - "q": { - "select": [ - "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8462-4').valueQuantity.value as data", - "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8462-4').valueQuantity.unit as unit" - ], - "from": "Observation", - "where": { - "componentCode": "http://loinc.org|8462-4" - } - }, - "dataset_options": { - "label": "Diastolic" - } - }, - { - "q": { - "select": [ - "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8480-6').valueQuantity.value as data", - "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8480-6').valueQuantity.unit as unit" - ], - "from": "Observation", - "where": { - "componentCode": "http://loinc.org|8480-6" - } - }, - "dataset_options": { - "label": "Systolic" - } - }], - "parsing": { - "xAxisKey": "id", - "yAxisKey": "data" - } + "height": 6, + "item_type": "records-summary-widget" }, { "title_text": "Patient Vitals", "description_text": "", - "x": 0, - "y": 5, + "x": 8, + "y": 0, "width": 4, - "height": 5, + "height": 6, "item_type": "patient-vitals-widget" }, + { "title_text": "Observations by Type", "description_text": "", - "x": 4, - "y": 5, + "x": 0, + "y": 6, "width": 8, "height": 5, "item_type": "donut-chart-widget", @@ -141,11 +46,113 @@ "key": "value" } }, + + { + "title_text": "Weight", + "description_text": "", + "x": 8, + "y": 6, + "width": 2, + "height": 2, + "item_type": "simple-line-chart-widget", + "queries": [{ + "q": { + "select": [ + "valueQuantity.value as data", + "valueQuantity.unit as unit", + "(effectiveDateTime | issued).first() as label" + ], + "from": "Observation", + "where": { + "code": "http://loinc.org|29463-7,http://loinc.org|3141-9,http://snomed.info/sct|27113001" + }, + "limit": 50 + } + }], + "parsing": { + "xAxisKey": "label", + "yAxisKey": "data" + } + }, + { + "title_text": "Height", + "description_text": "", + "x": 10, + "y": 6, + "width": 2, + "height": 2, + "item_type": "simple-line-chart-widget", + "queries": [{ + "q": { + "select": [ + "valueQuantity.value as data", + "valueQuantity.unit as unit", + "(effectiveDateTime | issued).first() as label" + ], + "from": "Observation", + "where": { + "code": "http://loinc.org|8302-2" + }, + "limit": 50 + } + }], + "parsing": { + "xAxisKey": "label", + "yAxisKey": "data" + } + }, + { + "title_text": "Blood Pressure", + "description_text": "How much pressure your blood is exerting against your artery walls when the heart beats", + "x": 8, + "y": 8, + "width": 4, + "height": 3, + "item_type": "grouped-bar-chart-widget", + "queries": [ + { + "q": { + "select": [ + "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8462-4').valueQuantity.value as data", + "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8462-4').valueQuantity.unit as unit" + ], + "from": "Observation", + "where": { + "componentCode": "http://loinc.org|8462-4" + }, + "limit": 50 + }, + "dataset_options": { + "label": "Diastolic" + } + }, + { + "q": { + "select": [ + "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8480-6').valueQuantity.value as data", + "component.where(code.coding.system = 'http://loinc.org' and code.coding.code = '8480-6').valueQuantity.unit as unit" + ], + "from": "Observation", + "where": { + "componentCode": "http://loinc.org|8480-6" + }, + "limit": 50 + }, + "dataset_options": { + "label": "Systolic" + } + }], + "parsing": { + "xAxisKey": "id", + "yAxisKey": "data" + } + }, + { "title_text": "Compliance", "description_text": "Use to track important healthcare and medical tasks.", "x": 0, - "y": 10, + "y": 11, "width": 4, "height": 2, "item_type": "dual-gauges-widget", @@ -184,7 +191,7 @@ "title_text": "Recent Encounters", "description_text": "Recent interactions with healthcare providers", "x": 4, - "y": 10, + "y": 11, "width": 8, "height": 4, "item_type": "table-widget", @@ -197,7 +204,8 @@ "participant.individual.display as provider" ], "from": "Encounter", - "where": {} + "where": {}, + "limit": 50 } }], "parsing": { diff --git a/backend/pkg/web/handler/dashboard/secondary.json b/backend/pkg/web/handler/dashboard/secondary.json new file mode 100644 index 00000000..0d7946bb --- /dev/null +++ b/backend/pkg/web/handler/dashboard/secondary.json @@ -0,0 +1,26 @@ +{ + "id": "secondary", + "schema_version": "1.0", + "title": "Secondary Dashboard", + "description": "An second dashboard to show-off the flexibility of the dashboard system.", + "widgets": [ + { + "title_text": "Records Summary", + "description_text": "Track key metrics for your chronic disease (eg. Diabetes). The data within this widget is not reflective of your health record, and is only present for demonstrational purposes.", + "x": 0, + "y": 0, + "width": 8, + "height": 6, + "item_type": "records-summary-widget" + }, + { + "title_text": "Care Team", + "description_text": "Track key metrics for your chronic disease (eg. Diabetes). The data within this widget is not reflective of your health record, and is only present for demonstrational purposes.", + "x": 8, + "y": 0, + "width": 4, + "height": 6, + "item_type": "image-list-group-widget" + } + ] +} diff --git a/frontend/src/app/components/gridstack/gridstack.component.ts b/frontend/src/app/components/gridstack/gridstack.component.ts index 940b178d..b0dc1e32 100644 --- a/frontend/src/app/components/gridstack/gridstack.component.ts +++ b/frontend/src/app/components/gridstack/gridstack.component.ts @@ -11,15 +11,7 @@ import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridSta import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component'; import {CommonModule} from '@angular/common'; -import {WidgetsModule} from '../../widgets/widgets.module'; -import {ComplexLineWidgetComponent} from '../../widgets/complex-line-widget/complex-line-widget.component'; -import {DashboardWidgetComponent} from '../../widgets/dashboard-widget/dashboard-widget.component'; -import {DonutChartWidgetComponent} from '../../widgets/donut-chart-widget/donut-chart-widget.component'; -import {DualGaugesWidgetComponent} from '../../widgets/dual-gauges-widget/dual-gauges-widget.component'; -import {GroupedBarChartWidgetComponent} from '../../widgets/grouped-bar-chart-widget/grouped-bar-chart-widget.component'; -import {PatientVitalsWidgetComponent} from '../../widgets/patient-vitals-widget/patient-vitals-widget.component'; -import {SimpleLineChartWidgetComponent} from '../../widgets/simple-line-chart-widget/simple-line-chart-widget.component'; -import {TableWidgetComponent} from '../../widgets/table-widget/table-widget.component'; +import {WidgetsModule, WidgetComponents} from '../../widgets/widgets.module'; import {DashboardWidgetComponentInterface} from '../../widgets/dashboard-widget-component-interface'; /** events handlers emitters signature for different events */ @@ -137,16 +129,7 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy { ) { // register all our dynamic components created in the grid - GridstackComponent.addComponentToSelectorType([ - ComplexLineWidgetComponent, - DashboardWidgetComponent, - DonutChartWidgetComponent, - DualGaugesWidgetComponent, - GroupedBarChartWidgetComponent, - PatientVitalsWidgetComponent, - SimpleLineChartWidgetComponent, - TableWidgetComponent, - ]); + GridstackComponent.addComponentToSelectorType(WidgetComponents()); // set globally our method to create the right widget type GridStack.addRemoveCB = gsCreateNgComponents; GridStack.saveCB = gsSaveAdditionalNgInfo; diff --git a/frontend/src/app/models/widget/dashboard-widget-config.ts b/frontend/src/app/models/widget/dashboard-widget-config.ts index 0f0fed36..c00d735a 100644 --- a/frontend/src/app/models/widget/dashboard-widget-config.ts +++ b/frontend/src/app/models/widget/dashboard-widget-config.ts @@ -3,7 +3,7 @@ import * as _ from 'lodash'; export class DashboardWidgetConfig { id?: string - item_type: "complex-line-widget" | "donut-chart-widget" | "dual-gauges-widget" | "grouped-bar-chart-widget" | "patient-vitals-widget" | "simple-line-chart-widget" | "table-widget" + item_type: "image-list-group-widget" | "complex-line-widget" | "donut-chart-widget" | "dual-gauges-widget" | "grouped-bar-chart-widget" | "patient-vitals-widget" | "simple-line-chart-widget" | "table-widget" | "records-summary-widget" title_text: string description_text: string diff --git a/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.html b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.html new file mode 100644 index 00000000..66d790d5 --- /dev/null +++ b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.scss b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.spec.ts b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.spec.ts new file mode 100644 index 00000000..e385d67e --- /dev/null +++ b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImageListGroupWidgetComponent } from './image-list-group-widget.component'; + +describe('ImageListGroupWidgetComponent', () => { + let component: ImageListGroupWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImageListGroupWidgetComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ImageListGroupWidgetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.ts b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.ts new file mode 100644 index 00000000..474598de --- /dev/null +++ b/frontend/src/app/widgets/image-list-group-widget/image-list-group-widget.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import {NgChartsModule} from 'ng2-charts'; +import {CommonModule} from '@angular/common'; +import {LoadingWidgetComponent} from '../loading-widget/loading-widget.component'; +import {EmptyWidgetComponent} from '../empty-widget/empty-widget.component'; +import {DashboardWidgetComponent} from '../dashboard-widget/dashboard-widget.component'; +import {DashboardWidgetConfig} from '../../models/widget/dashboard-widget-config'; + +@Component({ + standalone: true, + imports: [NgChartsModule, CommonModule, LoadingWidgetComponent, EmptyWidgetComponent], + selector: 'image-list-group-widget', + templateUrl: './image-list-group-widget.component.html', + styleUrls: ['./image-list-group-widget.component.scss'] +}) +export class ImageListGroupWidgetComponent extends DashboardWidgetComponent implements OnInit { + + ngOnInit(): void { + //manually define the widget config, rather than pull from the configuration file + this.widgetConfig = { + id: 'image-list-group-widget', + item_type: 'image-list-group-widget', + description_text: 'Displays a summary of patient records', + width: 4, + height: 5, + title_text: 'Medical Records', + queries: [] + + } as DashboardWidgetConfig + super.ngOnInit(); + this.loading = false + this.isEmpty = false + } + +} diff --git a/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.html b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.html new file mode 100644 index 00000000..9a470098 --- /dev/null +++ b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.html @@ -0,0 +1,47 @@ + + + + + + + + + + + +
+
+
Medical Records
+
+
+ +
+
+
+ + + + + + + + +
+
+
+
{{groupInfo.displayName}}
+ + + {{resourceTypes}}
+
+
+
+
{{groupInfo.count}}
+ Records +
+
+
+
+
+
+
diff --git a/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.scss b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.scss new file mode 100644 index 00000000..5687026f --- /dev/null +++ b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.scss @@ -0,0 +1,14 @@ +tbody { + display: grid; + grid-template-columns: repeat(2, auto); +} + +/* Borders */ +/* The margin declarations are used to simulate border-collapse:collapse */ +tr { + margin: 0 0 -1px -1px; + //border: 1px solid black; +} +tbody:not(:empty) { + margin: 0 0 1px 1px; +} diff --git a/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.spec.ts b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.spec.ts new file mode 100644 index 00000000..45cbf865 --- /dev/null +++ b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RecordsSummaryWidgetComponent } from './records-summary-widget.component'; + +describe('RecordsSummaryWidgetComponent', () => { + let component: RecordsSummaryWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ RecordsSummaryWidgetComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RecordsSummaryWidgetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.ts b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.ts new file mode 100644 index 00000000..ce72c9bc --- /dev/null +++ b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.component.ts @@ -0,0 +1,214 @@ +import { Component, OnInit } from '@angular/core'; +import {NgChartsModule} from 'ng2-charts'; +import {CommonModule} from '@angular/common'; +import {MomentModule} from 'ngx-moment'; +import {LoadingWidgetComponent} from '../loading-widget/loading-widget.component'; +import {EmptyWidgetComponent} from '../empty-widget/empty-widget.component'; +import {DashboardWidgetComponent} from '../dashboard-widget/dashboard-widget.component'; +import {DashboardWidgetConfig} from '../../models/widget/dashboard-widget-config'; +import {Summary} from '../../models/fasten/summary'; + +class GroupedSummary { + displayName: string + imageName: string + resourceTypes: string[] + includedResourceTypes: string[] = [] + count: number = 0 +} + + +@Component({ + standalone: true, + imports: [CommonModule, LoadingWidgetComponent, EmptyWidgetComponent], + selector: 'records-summary-widget', + templateUrl: './records-summary-widget.component.html', + styleUrls: ['./records-summary-widget.component.scss'] +}) +export class RecordsSummaryWidgetComponent extends DashboardWidgetComponent implements OnInit { + + // constructor() { } + + summary: Summary + + groupLookup: GroupedSummary[] = [ + { + displayName: 'Allergies', + imageName: 'allergies', + resourceTypes: ['AllergyIntolerance', 'AdverseEvent'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Care Team', + imageName: 'care-team', + resourceTypes: ['CareTeam', 'Practitioner', 'Patient', 'RelatedPerson', 'PractitionerRole'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Clinical Notes', + imageName: 'clinical-notes', + resourceTypes: ['DocumentReference', 'DiagnosticReport'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Files', + imageName: 'files', + resourceTypes: ['Binary', 'Media'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Lab Results', + imageName: 'lab-results', + resourceTypes: ['Observation', 'Specimen'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Health Issues', + imageName: 'health-issues', + resourceTypes: ['Conditions', 'Encounters'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Facilities', + imageName: 'facilities', + resourceTypes: ['Organization', 'Location'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Health Goals', + imageName: 'health-goals', + resourceTypes: ['Goal'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Health Insurance', + imageName: 'health-insurance', + resourceTypes: ['Coverage', 'ExplanationOfBenefit', 'Claim'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Health Assessments', + imageName: 'health-assessments', + resourceTypes: ['QuestionnaireResponse','Questionnaire', 'CarePlan', 'FamilyMemberHistory'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Immunizations', + imageName: 'immunizations', + resourceTypes: ['Immunization'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Implants', + imageName: 'implants', + resourceTypes: ['Device'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Medications', + imageName: 'medications', + resourceTypes: ['Medication', 'MedicationRequest', 'MedicationStatement', 'MedicationAdministration', 'MedicationDispense'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Demographics', + imageName: 'demographics', + resourceTypes: ['Patient'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Procedures', + imageName: 'procedures', + resourceTypes: ['Procedure','ServiceRequest'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Provenance', + imageName: 'provenance', + resourceTypes: ['Provenance'], + includedResourceTypes:[], + count: 0 + }, + { + displayName: 'Appointments', + imageName: 'appointments', + resourceTypes: ['Appointment', 'Schedule', 'Slot'], + includedResourceTypes:[], + count: 0 + } + ] + + ngOnInit(): void { + //manually define the widget config, rather than pull from the configuration file + this.widgetConfig = { + id: 'records-summary-widget', + item_type: 'records-summary-widget', + description_text: 'Displays a summary of patient records', + width: 4, + height: 5, + title_text: 'Medical Records', + queries: [ + ] + + } as DashboardWidgetConfig + super.ngOnInit(); + this.chartProcessQueryResults(null) + } + + chartProcessQueryResults(queryResults: any[]) { + + this.fastenApi.getSummary().subscribe((summary: Summary) => { + this.summary = summary + for (let resourceTypeCount of summary.resource_type_counts) { + let foundGroup = false + for (let groupKey in this.groupLookup) { + let group = this.groupLookup[groupKey] + if (group.resourceTypes.indexOf(resourceTypeCount.resource_type) > -1) { + foundGroup = true + this.groupLookup[groupKey].count += resourceTypeCount.count + this.groupLookup[groupKey].includedResourceTypes.push(resourceTypeCount.resource_type) + } + } + + if (!foundGroup) { + this.groupLookup[resourceTypeCount.resource_type] = { + displayName: resourceTypeCount.resource_type, + + resourceTypes: [resourceTypeCount.resource_type], + count: resourceTypeCount.count + } + } + } + + //filter any groups with 0 counts + this.groupLookup = this.groupLookup.filter((group) => { + return group.count > 0 + }) + + if(this.summary.resource_type_counts.length > 0){ + this.isEmpty = false + } + this.loading = false + }, + (error) => { + this.loading = false + }, + () => { + console.log('completed getting summary') + }) + } +} diff --git a/frontend/src/app/widgets/records-summary-widget/records-summary-widget.stories.ts b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.stories.ts new file mode 100644 index 00000000..ba19076c --- /dev/null +++ b/frontend/src/app/widgets/records-summary-widget/records-summary-widget.stories.ts @@ -0,0 +1,50 @@ +import type { Meta, StoryObj } from '@storybook/angular'; +import {RecordsSummaryWidgetComponent} from './records-summary-widget.component'; +import {applicationConfig, moduleMetadata} from '@storybook/angular'; +import {HttpClient, HttpClientModule} from '@angular/common/http'; +import {HTTP_CLIENT_TOKEN} from '../../dependency-injection'; +import {importProvidersFrom} from '@angular/core'; +import {CommonModule} from '@angular/common'; + +// More on how to set up stories at: https://storybook.js.org/docs/angular/writing-stories/introduction +const meta: Meta = { + title: 'Widget/RecordsSummaryWidget', + component: RecordsSummaryWidgetComponent, + decorators: [ + applicationConfig({ + providers: [ + { + provide: HttpClient, + useClass: HttpClient + }, + { + provide: HTTP_CLIENT_TOKEN, + useClass: HttpClient, + }, + importProvidersFrom(HttpClientModule) + ] + }), + moduleMetadata({ + imports: [CommonModule, HttpClientModule], + }) + ], + tags: ['autodocs'], + render: (args: RecordsSummaryWidgetComponent) => ({ + props: { + backgroundColor: null, + ...args, + }, + }), + argTypes: { + }, +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/angular/writing-stories/args +export const Example: Story = { + args: { + } +}; + diff --git a/frontend/src/app/widgets/widgets.module.ts b/frontend/src/app/widgets/widgets.module.ts index 61aaae00..f8b12668 100644 --- a/frontend/src/app/widgets/widgets.module.ts +++ b/frontend/src/app/widgets/widgets.module.ts @@ -1,4 +1,4 @@ -import { NgModule } from '@angular/core'; +import {Component, NgModule, Type} from '@angular/core'; import {ComplexLineWidgetComponent} from './complex-line-widget/complex-line-widget.component'; import {DonutChartWidgetComponent} from './donut-chart-widget/donut-chart-widget.component'; import {DualGaugesWidgetComponent} from './dual-gauges-widget/dual-gauges-widget.component'; @@ -9,6 +9,8 @@ import {TableWidgetComponent} from './table-widget/table-widget.component'; import { LoadingWidgetComponent } from './loading-widget/loading-widget.component'; import { EmptyWidgetComponent } from './empty-widget/empty-widget.component'; import {DashboardWidgetComponent} from './dashboard-widget/dashboard-widget.component'; +import { RecordsSummaryWidgetComponent } from './records-summary-widget/records-summary-widget.component'; +import { ImageListGroupWidgetComponent } from './image-list-group-widget/image-list-group-widget.component'; @NgModule({ imports: [ @@ -18,6 +20,8 @@ import {DashboardWidgetComponent} from './dashboard-widget/dashboard-widget.comp DualGaugesWidgetComponent, GroupedBarChartWidgetComponent, PatientVitalsWidgetComponent, + RecordsSummaryWidgetComponent, + ImageListGroupWidgetComponent, SimpleLineChartWidgetComponent, TableWidgetComponent, LoadingWidgetComponent, @@ -32,6 +36,8 @@ import {DashboardWidgetComponent} from './dashboard-widget/dashboard-widget.comp DualGaugesWidgetComponent, GroupedBarChartWidgetComponent, PatientVitalsWidgetComponent, + RecordsSummaryWidgetComponent, + ImageListGroupWidgetComponent, SimpleLineChartWidgetComponent, TableWidgetComponent, LoadingWidgetComponent, @@ -41,3 +47,21 @@ import {DashboardWidgetComponent} from './dashboard-widget/dashboard-widget.comp }) export class WidgetsModule { } + +//when adding widgets to this list, you must also register the widget id in +// frontend/src/app/models/widget/dashboard-widget-config.ts +export function WidgetComponents(): Type[] { + return [ + ComplexLineWidgetComponent, + DonutChartWidgetComponent, + DualGaugesWidgetComponent, + GroupedBarChartWidgetComponent, + PatientVitalsWidgetComponent, + RecordsSummaryWidgetComponent, + ImageListGroupWidgetComponent, + SimpleLineChartWidgetComponent, + TableWidgetComponent, + LoadingWidgetComponent, + EmptyWidgetComponent + ] +} diff --git a/frontend/src/assets/icons/allergies.svg b/frontend/src/assets/icons/allergies.svg new file mode 100644 index 00000000..91afa991 --- /dev/null +++ b/frontend/src/assets/icons/allergies.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/care-plans.svg b/frontend/src/assets/icons/care-plans.svg new file mode 100644 index 00000000..54606699 --- /dev/null +++ b/frontend/src/assets/icons/care-plans.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/care-team.svg b/frontend/src/assets/icons/care-team.svg new file mode 100644 index 00000000..0682ff46 --- /dev/null +++ b/frontend/src/assets/icons/care-team.svg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/clinical-notes.svg b/frontend/src/assets/icons/clinical-notes.svg new file mode 100644 index 00000000..b6e568f1 --- /dev/null +++ b/frontend/src/assets/icons/clinical-notes.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/demographics.svg b/frontend/src/assets/icons/demographics.svg new file mode 100644 index 00000000..58659850 --- /dev/null +++ b/frontend/src/assets/icons/demographics.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/facilities.svg b/frontend/src/assets/icons/facilities.svg new file mode 100644 index 00000000..2f81204d --- /dev/null +++ b/frontend/src/assets/icons/facilities.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/files.svg b/frontend/src/assets/icons/files.svg new file mode 100644 index 00000000..b6e568f1 --- /dev/null +++ b/frontend/src/assets/icons/files.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/health-assessments.svg b/frontend/src/assets/icons/health-assessments.svg new file mode 100644 index 00000000..9880d617 --- /dev/null +++ b/frontend/src/assets/icons/health-assessments.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/health-goals.svg b/frontend/src/assets/icons/health-goals.svg new file mode 100644 index 00000000..54606699 --- /dev/null +++ b/frontend/src/assets/icons/health-goals.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/health-issues.svg b/frontend/src/assets/icons/health-issues.svg new file mode 100644 index 00000000..16c1e624 --- /dev/null +++ b/frontend/src/assets/icons/health-issues.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/immunizations.svg b/frontend/src/assets/icons/immunizations.svg new file mode 100644 index 00000000..7962104f --- /dev/null +++ b/frontend/src/assets/icons/immunizations.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/implants.svg b/frontend/src/assets/icons/implants.svg new file mode 100644 index 00000000..7e2e9f57 --- /dev/null +++ b/frontend/src/assets/icons/implants.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/lab-results.svg b/frontend/src/assets/icons/lab-results.svg new file mode 100644 index 00000000..316eba2f --- /dev/null +++ b/frontend/src/assets/icons/lab-results.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/medications.svg b/frontend/src/assets/icons/medications.svg new file mode 100644 index 00000000..86ba60fc --- /dev/null +++ b/frontend/src/assets/icons/medications.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/procedures.svg b/frontend/src/assets/icons/procedures.svg new file mode 100644 index 00000000..6bdd9345 --- /dev/null +++ b/frontend/src/assets/icons/procedures.svg @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/frontend/src/assets/icons/proxyinformation.svg b/frontend/src/assets/icons/proxyinformation.svg new file mode 100644 index 00000000..49685a4d --- /dev/null +++ b/frontend/src/assets/icons/proxyinformation.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/visits.svg b/frontend/src/assets/icons/visits.svg new file mode 100644 index 00000000..08450155 --- /dev/null +++ b/frontend/src/assets/icons/visits.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/frontend/src/assets/icons/vitalsigns.svg b/frontend/src/assets/icons/vitalsigns.svg new file mode 100644 index 00000000..4e4f7ece --- /dev/null +++ b/frontend/src/assets/icons/vitalsigns.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + +