Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) Add remote-select question type and migrate data sources to o3 schema format #157

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,22 @@ export class RemoteSelectComponent implements OnInit, ControlValueAccessor {

denniskigen marked this conversation as resolved.
Show resolved Hide resolved
private loadOptions() {
this.remoteOptions$ = concat(
of([]), // default items
this.dataSource.searchOptions(
'',
this.dataSource?.dataSourceOptions ?? {}
) ?? of([]), // default items
denniskigen marked this conversation as resolved.
Show resolved Hide resolved
this.remoteOptionInput$.pipe(
distinctUntilChanged(),
tap(() => {
this.loading = true;
}),
switchMap((term) =>
this.dataSource.searchOptions(term).pipe(
catchError(() => of([])), // empty list on error
tap(() => (this.loading = false))
)
this.dataSource
.searchOptions(term, this.dataSource?.dataSourceOptions ?? {})
.pipe(
catchError(() => of([])), // empty list on error
tap(() => (this.loading = false))
)
denniskigen marked this conversation as resolved.
Show resolved Hide resolved
)
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { MinLengthValidationModel } from '../question-models/min-length-validati
import { WorkspaceLauncherQuestion } from '../question-models';
import { DecimalValidationModel } from '../question-models/decimal-validation.model';
import { DisallowDecimalsValidationModel } from '../question-models/disallow-decimals-validation.model';
import { RemoteSelectQuestion } from '../question-models/remote-select-question';
@Injectable()
export class QuestionFactory {
dataSources: any = {};
Expand Down Expand Up @@ -745,6 +746,52 @@ export class QuestionFactory {
return question;
}

toRemoteSelectQuestion(schemaQuestion: any): RemoteSelectQuestion {
const dataSource = this.getDataSourceConfig(schemaQuestion);
const question = new RemoteSelectQuestion({
dataSource: dataSource.name,
dataSourceOptions: dataSource.options,
type: '',
key: ''
});
question.questionIndex = this.quetionIndex;
question.label = schemaQuestion.label;
question.prefix = schemaQuestion.prefix;
question.key = schemaQuestion.id;
question.renderingType = 'remote-select';
question.validators = this.addValidators(schemaQuestion);
question.extras = schemaQuestion;

const mappings: any = {
denniskigen marked this conversation as resolved.
Show resolved Hide resolved
label: 'label',
required: 'required',
id: 'key'
};

question.componentConfigs = schemaQuestion.componentConfigs || [];
this.copyProperties(mappings, schemaQuestion, question);
this.addDisableOrHideProperty(schemaQuestion, question);
this.addAlertProperty(schemaQuestion, question);
this.addHistoricalExpressions(schemaQuestion, question);
this.addCalculatorProperty(schemaQuestion, question);

return question;
}

private getDataSourceConfig(
schemaQuestion: any
): { name: string; options: any } {
denniskigen marked this conversation as resolved.
Show resolved Hide resolved
const dataSourceName = schemaQuestion.questionOptions?.dataSource;
const dataSourceOptions = schemaQuestion.questionOptions?.dataSourceOptions;
// See https://github.com/openmrs/openmrs-contrib-json-schemas/blob/main/form.schema.json
const openMrs3DataSource = schemaQuestion.questionOptions?.datasource;
denniskigen marked this conversation as resolved.
Show resolved Hide resolved

return {
name: dataSourceName ?? openMrs3DataSource?.name ?? '',
options: dataSourceOptions ?? openMrs3DataSource?.config ?? {}
};
}

toDecimalQuestion(schemaQuestion: any): TextInputQuestion {
const question = new TextInputQuestion({
placeholder: '',
Expand Down Expand Up @@ -938,6 +985,8 @@ export class QuestionFactory {
return this.toFileUploadQuestion(schema);
case 'workspace-launcher':
return this.toWorkspaceLauncher(schema);
case 'remote-select':
return this.toRemoteSelectQuestion(schema);
default:
console.warn('New Schema Question Type found.........' + renderType);
return this.toTextQuestion(schema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { SelectOption } from './select-option';
import { Observable } from 'rxjs';

export interface DataSource {
dataSourceOptions?: any;
dataSourceOptions?: Record<string, unknown>;
dataFromSourceChanged?: Observable<SelectOption[]>;
resolveSelectedValue(value): Observable<SelectOption>;
searchOptions(searchText): Observable<SelectOption[]>;
searchOptions(
searchText,
dataSourceOptions?: Record<string, unknown>
): Observable<SelectOption[]>;
fileUpload(data): Observable<any>;
fetchFile(url: string, fileType?: string): Observable<any>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { BaseOptions } from '../interfaces/base-options';

export interface RemoteSelectQuestionOptions extends BaseOptions {
dataSource: string;
dataSourceOptions?: Record<string, unknown>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class QuestionBase implements BaseOptions {
historicalDataValue?: any;
extras?: any;
dataSource?: string;
dataSourceOptions?: any;
dataSourceOptions?: Record<string, unknown>;

controlType?: AfeControlType;
validators?: Array<ValidationModel>;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { QuestionBase } from './question-base';
import { AfeControlType } from '../../abstract-controls-extension/afe-control-type';
import { RemoteSelectQuestionOptions } from './interfaces/remote-select-question-options';

export class RemoteSelectQuestion extends QuestionBase {
rendering: string;
options: any[];

constructor(options: RemoteSelectQuestionOptions) {
super(options);
this.renderingType = 'select';
this.controlType = AfeControlType.AfeFormControl;
this.dataSource = options.dataSource || '';
this.dataSourceOptions = options.dataSourceOptions || {};
}
}
50 changes: 45 additions & 5 deletions src/app/adult-1.6.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,23 @@
"rendering": "ui-select-extended"
}
},
{
"id": "admitToLocation",
"type": "obs",
"required": true,
"label": "Admit to location",
"questionOptions": {
"rendering": "remote-select",
"required": true,
"concept": "CIEL:169403",
"datasource": {
"name": "location_datasource",
"config": {
"tag": "Admission Location"
}
}
}
},
{
"type": "encounterLocation",
"label": "Facility name (site/satellite clinic required):",
Expand Down Expand Up @@ -3362,7 +3379,9 @@
"type": "obs",
"hide": {
"field": "tb_current",
"value": ["b8aa06ca-93c6-40ea-b144-c74f841926f4"]
"value": [
"b8aa06ca-93c6-40ea-b144-c74f841926f4"
]
},
"id": "__ptxCzFD2s"
}
Expand Down Expand Up @@ -6991,7 +7010,9 @@
"type": "obs",
"hide": {
"field": "q26f",
"value": ["b8aa06ca-93c6-40ea-b144-c74f841926f4"]
"value": [
"b8aa06ca-93c6-40ea-b144-c74f841926f4"
]
},
"id": "__Jywyp94Lw"
}
Expand Down Expand Up @@ -8012,7 +8033,16 @@
"questionOptions": {
"concept": "318a5e8b-218c-4f66-9106-cd581dec1f95",
"rendering": "date",
"weeksList": [2, 4, 6, 8, 12, 16, 24, 36]
"weeksList": [
2,
4,
6,
8,
12,
16,
24,
36
]
},
"validators": [
{
Expand Down Expand Up @@ -8042,7 +8072,17 @@
"questionOptions": {
"concept": "a8a666ba-1350-11df-a1f1-0026b9348838",
"rendering": "date",
"weeksList": [2, 4, 6, 8, 12, 16, 20, 24, 36]
"weeksList": [
2,
4,
6,
8,
12,
16,
20,
24,
36
]
},
"validators": [
{
Expand Down Expand Up @@ -8178,4 +8218,4 @@
]
}
]
}
}
42 changes: 41 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export class AppComponent implements OnInit {
searchOptions: this.sampleSearch,
resolveSelectedValue: this.sampleResolve
});
this.dataSources.registerDataSource('location_datasource', {
searchOptions: this.sampleLocationSearch,
resolveSelectedValue: this.sampleResolve
});
this.dataSources.registerDataSource('provider', {
searchOptions: this.sampleSearch,
resolveSelectedValue: this.sampleResolve
Expand Down Expand Up @@ -350,7 +354,43 @@ export class AppComponent implements OnInit {
});
}

public sampleSearch(): Observable<any> {
public sampleLocationSearch(
searchText: string
): Observable<Array<Record<string, string>>> {
const locations = [
{
value: 'ba685651-ed3b-4e63-9b35-78893060758a',
label: 'Inpatient Ward'
},
{
value: '184ac7d9-225a-41f8-bac7-c87b1327e1b0',
label: 'Ward 1'
},
{
value: '5a7f3c53-6bb4-448b-a966-5e65b397b9f3',
label: 'Ward 2'
},
{
value: '2272b8cd-b690-4878-a50c-40d22235b3f3',
label: 'Ward 3'
},
{
value: 'db0253bb-8e2e-4b2c-b60c-6c88110e3c2e',
label: 'Duplix'
}
];
if (searchText) {
return of(
locations.filter((location) =>
location.label.toLowerCase().includes(searchText.toLowerCase())
)
);
}

return of(locations);
denniskigen marked this conversation as resolved.
Show resolved Hide resolved
}

public sampleSearch(searchText: string): Observable<any> {
const items: Array<any> = [
{ value: '0', label: 'Aech' },
{ value: '5b6e58ea-1359-11df-a1f1-0026b9348838', label: 'Art3mis' },
Expand Down
25 changes: 17 additions & 8 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import {
provideHttpClient,
withInterceptorsFromDi
} from '@angular/common/http';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormEntryModule } from '@openmrs/ngx-formentry';
import { AppComponent } from './app.component';
import { NgxTranslateModule } from './translate/translate.module';

@NgModule({ declarations: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [AppComponent], imports: [BrowserModule,
BrowserAnimationsModule,
FormEntryModule,
ReactiveFormsModule,
NgxTranslateModule], providers: [provideHttpClient(withInterceptorsFromDi())] })
@NgModule({
declarations: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
bootstrap: [AppComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
FormEntryModule,
ReactiveFormsModule,
NgxTranslateModule
],
providers: [provideHttpClient(withInterceptorsFromDi())]
})
export class AppModule {}
Loading