diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index 4cab1b795..be98afa37 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -247,6 +247,14 @@ const appRoutes: Routes = [ title: 'GENERAL.TITLES.SUPPORTIVE-MATERIAL' }, }, + { + path: 'reference-type', + loadChildren: () => import('./ui/admin/reference-type/reference-type.module').then(m => m.ReferenceTypeModule), + data: { + breadcrumb: true, + title: 'GENERAL.TITLES.REFERENCE-TYPE' + }, + }, { path: 'index-managment', loadChildren: () => import('./ui/admin/index-managment/index-managment.module').then(m => m.IndexManagmentModule), diff --git a/dmp-frontend/src/app/core/common/enum/permission.enum.ts b/dmp-frontend/src/app/core/common/enum/permission.enum.ts index 5ec91c37f..eace39451 100644 --- a/dmp-frontend/src/app/core/common/enum/permission.enum.ts +++ b/dmp-frontend/src/app/core/common/enum/permission.enum.ts @@ -18,5 +18,10 @@ export enum AppPermission { ViewDescriptionTemplateTypePage = "ViewDescriptionTemplateTypePage", ViewDmpBlueprintPage = "ViewDmpBlueprintPage", ViewDescriptionTemplatePage = "ViewDescriptionTemplatePage", + + //ReferenceType + BrowseReferenceType = "BrowseReferenceType", + EditReferenceType = "EditReferenceType", + DeleteReferenceType = "DeleteReferenceType", } diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index e243a9cc9..902343733 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -58,6 +58,7 @@ import { UserSettingsService } from './services/user-settings/user-settings.serv import { FileUtils } from './services/utilities/file-utils.service'; import { QueryParamsService } from './services/utilities/query-params.service'; import { DescriptionTemplateService } from './services/description-template/description-template.service'; +import { ReferenceTypeService } from './services/reference-type/reference-type.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -134,7 +135,8 @@ export class CoreServiceModule { FilterService, FileUtils, ReferenceService, - DescriptionTemplateService + DescriptionTemplateService, + ReferenceTypeService ], }; } diff --git a/dmp-frontend/src/app/core/model/reference-type/reference-type.ts b/dmp-frontend/src/app/core/model/reference-type/reference-type.ts new file mode 100644 index 000000000..5872dae7b --- /dev/null +++ b/dmp-frontend/src/app/core/model/reference-type/reference-type.ts @@ -0,0 +1,132 @@ +import { IsActive } from "@app/core/common/enum/is-active.enum"; +import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; +import { Guid } from "@common/types/guid"; + +export interface ReferenceType { + id: Guid; + name: string; + code: string; + definition: ReferenceTypeDefinition; + isActive: IsActive; + createdAt: Date; + updatedAt: Date; +} + +export interface ReferenceTypeDefinition{ + fields: ReferenceTypeField[]; + externalApiConfig: ReferenceTypeExternalApiConfiguration[]; +} + +export interface ReferenceTypeField { + code: string; + dataType: ReferenceFieldDataType; +} + +export interface ReferenceTypeExternalApiConfiguration { + key: string; + label: string; + ordinal: number; + url: string; + results: ResultsConfiguration; + type: string; + paginationPath: string; + contentType: string; + //funderQuery?: string; + firstPage: string; + requestType?: string; + requestBody?: string; + filterType?: string; + auth?: AuthenticationConfiguration; + queries?: QueryConfig[]; +} + +export interface ResultsConfiguration{ + resultsArrayPath: string; + fieldsMapping: ResultFieldsMappingConfiguration[]; +} + + +export interface ResultFieldsMappingConfiguration{ + code: string; + responsePath: string; +} + +export interface AuthenticationConfiguration{ + authUrl: string; + authMethod: string; + authTokenPath: string; + authRequestBody: string; + type: string; +} + +export interface QueryConfig{ + condition: string; + separator: string; + value: string; + ordinal: number; +} + +// Persist + +export interface ReferenceTypePersist { + id?: Guid; + name: string; + code: string; + definition: ReferenceTypeDefinitionPersist; +} + +export interface ReferenceTypeDefinitionPersist{ + fields: ReferenceTypeFieldPersist[]; + externalApiConfig: ReferenceTypeExternalApiConfigurationPersist[]; +} + +export interface ReferenceTypeFieldPersist { + code: string; + dataType: ReferenceFieldDataType; +} + +export interface ReferenceTypeExternalApiConfigurationPersist { + key: string; + label: string; + ordinal: number; + url: string; + results: ResultsConfigurationPersist; + type: string; + paginationPath: string; + contentType: string; + // toDo move to a general query + //funderQuery?: string; + firstPage: string; + requestType?: string; + requestBody?: string; + filterType?: string; + auth?: AuthenticationConfigurationPersist; + queries?: QueryConfigPersist[]; +} + +export interface ResultsConfigurationPersist{ + resultsArrayPath: string; + fieldsMapping: ResultFieldsMappingConfigurationPersist[]; +} + + + +export interface ResultFieldsMappingConfigurationPersist{ + code: string; + responsePath: string; +} + +export interface AuthenticationConfigurationPersist{ + authUrl: string; + authMethod?: string; + authTokenPath: string; + authRequestBody: string; + type: string; +} + +export interface QueryConfigPersist{ + condition: string; + separator: string; + value: string; + ordinal: number; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/query/reference-type.lookup.ts b/dmp-frontend/src/app/core/query/reference-type.lookup.ts new file mode 100644 index 000000000..fba54d129 --- /dev/null +++ b/dmp-frontend/src/app/core/query/reference-type.lookup.ts @@ -0,0 +1,23 @@ +import { Lookup } from '@common/model/lookup'; +import { Guid } from '@common/types/guid'; +import { IsActive } from '../common/enum/is-active.enum'; + +export class ReferenceTypeLookup extends Lookup implements ReferenceTypeFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + codes: string[]; + + constructor() { + super(); + } +} + +export interface ReferenceTypeFilter { + ids: Guid[]; + excludedIds: Guid[]; + like: string; + isActive: IsActive[]; + codes: string[]; +} diff --git a/dmp-frontend/src/app/core/services/reference-type/reference-type.service.ts b/dmp-frontend/src/app/core/services/reference-type/reference-type.service.ts new file mode 100644 index 000000000..b83bd3539 --- /dev/null +++ b/dmp-frontend/src/app/core/services/reference-type/reference-type.service.ts @@ -0,0 +1,54 @@ +import { Injectable } from '@angular/core'; +import { QueryResult } from '@common/model/query-result'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { Observable, throwError } from 'rxjs'; +import { catchError, map } from 'rxjs/operators'; +import { ConfigurationService } from '../configuration/configuration.service'; +import { BaseHttpV2Service } from '../http/base-http-v2.service'; +import { ReferenceTypeLookup } from '@app/core/query/reference-type.lookup'; +import { ReferenceType, ReferenceTypePersist } from '@app/core/model/reference-type/reference-type'; + +@Injectable() +export class ReferenceTypeService { + + constructor( + private http: BaseHttpV2Service, + private configurationService: ConfigurationService, + private filterService: FilterService + ) { + } + + private get apiBase(): string { return `${this.configurationService.server}reference-type`; } + + query(q: ReferenceTypeLookup): Observable> { + const url = `${this.apiBase}/query`; + return this.http.post>(url, q).pipe(catchError((error: any) => throwError(error))); + } + + getSingle(id: Guid, reqFields: string[] = []): Observable { + const url = `${this.apiBase}/${id}`; + const options = { params: { f: reqFields } }; + + return this.http + .get(url, options).pipe( + catchError((error: any) => throwError(error))); + } + + persist(item: ReferenceTypePersist): Observable { + const url = `${this.apiBase}/persist`; + + return this.http + .post(url, item).pipe( + catchError((error: any) => throwError(error))); + } + + delete(id: Guid): Observable { + const url = `${this.apiBase}/${id}`; + + return this.http + .delete(url).pipe( + catchError((error: any) => throwError(error))); + } + +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html new file mode 100644 index 000000000..a16c89dac --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.html @@ -0,0 +1,409 @@ +
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.NEW' | translate}}

+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ + + + {{'REFERENCE-TYPE-EDITOR.NEW' | translate}} + + +
+
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.NAME' | translate}} + + + {{formGroup.get('name').getError('backendError').message}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CODE' | translate}} + + + {{formGroup.get('code').getError('backendError').message}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+ + + + + Reference Type Fields + + + + +
+ + +
+
+
+ +
+
+ {{'REFERENCE-TYPE-EDITOR.FIELDS.FIELD' | translate}} {{fieldIndex + 1}} +
+ + +
+ +
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CODE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.DATA-TYPE' | translate}} + + + {{dataType.name}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+
+
+
+
+
+
+
+ + + + Reference Type External API Configuration + + + +
+ +
+
+
+ +
+
+ {{'REFERENCE-TYPE-EDITOR.FIELDS.EXTERNAL-API-CONFIGURATION' | translate}} {{externalApiConfigIndex + 1}} +
+ + +
+ +
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.KEY' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.LABEL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.ORDINAL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Type + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.PAGINATION-PATH' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Content Type + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + First Page + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Request Type + + + {{request}} + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Request Body + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Filter Type + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ + +

{{'REFERENCE-TYPE-EDITOR.FIELDS.RESULTS' | translate}} + +

+
+ + Results Path + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.FIELD-MAPPING' | translate}} {{fieldMappingIndex + 1}}

+
+ +
+ +
+
+
+ +
+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.CODE' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Response Path + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+
+ +

{{'REFERENCE-TYPE-EDITOR.FIELDS.AUTHENTICATION' | translate}}

+
+ + {{'REFERENCE-TYPE-EDITOR.FIELDS.URL' | translate}} + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Method + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Token Path + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Request Body + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Type + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ + +

{{'REFERENCE-TYPE-EDITOR.FIELDS.QUERIES' | translate}} + +

+
+
+
+ +
+
+

{{'REFERENCE-TYPE-EDITOR.FIELDS.QUERY' | translate}} {{queryIndex + 1}}

+
+ +
+ +
+
+
+ +
+ + Condition + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Separator + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Value + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+ + Ordinal + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+
+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+ diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.scss b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.scss new file mode 100644 index 000000000..4372160ca --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.scss @@ -0,0 +1,116 @@ +.dmp-blueprint-editor { + margin-top: 1.3rem; + margin-left: 1em; + margin-right: 3em; + + .remove { + background-color: white; + color: black; + } + + .add { + background-color: white; + color: #009700; + } +} + +::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background, .mat-checkbox-indeterminate.mat-accent .mat-checkbox-background { + background-color: var(--primary-color-3); + // background-color: #0070c0; +} + +::ng-deep .mat-checkbox-disabled.mat-checkbox-checked .mat-checkbox-background, .mat-checkbox-disabled.mat-checkbox-indeterminate .mat-checkbox-background { + background-color: #b0b0b0; +} + +.finalize-btn { + border-radius: 30px; + border: 1px solid var(--primary-color); + background: transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + color: var(--primary-color); + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} + +.action-btn { + border-radius: 30px; + background-color: var(--secondary-color); + border: 1px solid transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + + transition-property: background-color, color; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} + +.dlt-section-btn { + margin: 0; + position: absolute; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +.section-input { + position: relative; +} + +.section-input .arrows { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); +} + +.action-list-item{ + display: flex; + align-items: center; + cursor: pointer; + + .action-list-icon{ + font-size: 1.2em; + // padding-right: 1em; + // width: 14px; + // margin-right: 0.5em; + // margin-left: -.09em; + // height: auto; + color: var(--primary-color); + } + + .action-list-text{ + font-size: 1em; + color: var(--primary-color); + } +} + +.field-delete{ + align-items: center; + display: flex; + cursor: pointer; + + .field-delete-icon{ + font-size: 1.2em; + width: 14px; + color: var(--primary-color); + } + + .field-delete-text{ + font-size: 1em; + margin-left: 0.5em; + color: var(--primary-color); + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts new file mode 100644 index 000000000..e4ff9be97 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.component.ts @@ -0,0 +1,281 @@ + +import { Component, OnInit } from '@angular/core'; +import { FormArray, UntypedFormGroup } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { DatePipe } from '@angular/common'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { ReferenceType, ReferenceTypePersist } from '@app/core/model/reference-type/reference-type'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { LoggingService } from '@app/core/services/logging/logging-service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { BaseEditor } from '@common/base/base-editor'; +import { FormService } from '@common/forms/form-service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { FilterService } from '@common/modules/text-filter/filter-service'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { map, takeUntil } from 'rxjs/operators'; +import { ReferenceTypeEditorResolver } from './reference-type-editor.resolver'; +import { ReferenceTypeEditorService } from './reference-type-editor.service'; +import { QueryConfigEditorModel, ReferenceTypeEditorModel, ReferenceTypeExternalApiConfigurationEditorModel, ReferenceTypeFieldEditorModel, ResultFieldsMappingConfigurationEditorModel } from './reference-type-editor.model'; +import { DmpBlueprintDefinitionSectionEditorModel } from '@app/ui/admin/dmp-blueprint/editor/dmp-blueprint-editor.model'; +import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type'; + + +export interface visiblesDataType { + name: string; + type: ReferenceFieldDataType; +} + +@Component({ + selector: 'app-reference-type-editor-component', + templateUrl: 'reference-type-editor.component.html', + styleUrls: ['./reference-type-editor.component.scss'], + providers: [ReferenceTypeEditorService] +}) +export class ReferenceTypeEditorComponent extends BaseEditor implements OnInit { + + isNew = true; + isDeleted = false; + formGroup: UntypedFormGroup = null; + showInactiveDetails = false; + + protected get canDelete(): boolean { + return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteReferenceType); + } + + protected get canSave(): boolean { + return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditReferenceType); + } + + protected get canFinalize(): boolean { + return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditReferenceType); + } + + + private hasPermission(permission: AppPermission): boolean { + return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission); + } + + constructor( + // BaseFormEditor injected dependencies + protected dialog: MatDialog, + protected language: TranslateService, + protected formService: FormService, + protected router: Router, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected filterService: FilterService, + protected datePipe: DatePipe, + protected route: ActivatedRoute, + protected queryParamsService: QueryParamsService, + // Rest dependencies. Inject any other needed deps here: + public authService: AuthService, + public enumUtils: EnumUtils, + private referenceTypeService: ReferenceTypeService, + private logger: LoggingService, + private referenceTypeEditorService: ReferenceTypeEditorService + ) { + super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService); + } + + visiblesDataTypes: visiblesDataType[] = [ + {name: "Text", type: ReferenceFieldDataType.Text}, + {name: "Date", type: ReferenceFieldDataType.Date}, + ] + + requestTypes: string[] = ["GET", "POST"] + + ngOnInit(): void { + super.ngOnInit(); + } + + getItem(itemId: Guid, successFunction: (item: ReferenceType) => void) { + this.referenceTypeService.getSingle(itemId, ReferenceTypeEditorResolver.lookupFields()) + .pipe(map(data => data as ReferenceType), takeUntil(this._destroyed)) + .subscribe( + data => successFunction(data), + error => this.onCallbackError(error) + ); + } + + prepareForm(data: ReferenceType) { + try { + this.editorModel = data ? new ReferenceTypeEditorModel().fromModel(data) : new ReferenceTypeEditorModel(); + this.isDeleted = data ? data.isActive === IsActive.Inactive : false; + this.buildForm(); + } catch (error) { + this.logger.error('Could not parse referenceType item: ' + data + error); + this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error); + } + } + + buildForm() { + this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditReferenceType)); + console.log(this.formGroup.get('definition')); + this.referenceTypeEditorService.setValidationErrorModel(this.editorModel.validationErrorModel); + } + + refreshData(): void { + this.getItem(this.editorModel.id, (data: ReferenceType) => this.prepareForm(data)); + } + + refreshOnNavigateToData(id?: Guid): void { + this.formGroup.markAsPristine(); + let route = []; + + if (id === null) { + route.push('../..'); + } else if (this.isNew) { + route.push('../' + id); + } else { + route.push('..'); + } + + this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route }); + } + + persistEntity(onSuccess?: (response) => void): void { + const formData = this.formService.getValue(this.formGroup.value) as ReferenceTypePersist; + console.log(formData); + // this.referenceTypeService.persist(formData) + // .pipe(takeUntil(this._destroyed)).subscribe( + // complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete), + // error => this.onCallbackError(error) + // ); + } + + formSubmit(): void { + this.formService.touchAllFormFields(this.formGroup); + if (!this.isFormValid()) { + return; + } + + this.persistEntity(); + } + + public delete() { + const value = this.formGroup.value; + if (value.id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + maxWidth: '300px', + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.referenceTypeService.delete(value.id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + clearErrorModel() { + this.editorModel.validationErrorModel.clear(); + this.formService.validateAllFormFields(this.formGroup); + } + + // + // + // fields + // + // + addField(): void { + const field: ReferenceTypeFieldEditorModel = new ReferenceTypeFieldEditorModel(); + (this.formGroup.get('definition').get('fields') as FormArray).push(field.buildForm()); + } + + removeField(fieldIndex: number): void { + (this.formGroup.get('definition').get('fields') as FormArray).removeAt(fieldIndex); + } + + dropFields(event: CdkDragDrop) { + const fieldssFormArray = (this.formGroup.get('definition').get('fields') as FormArray); + + moveItemInArray(fieldssFormArray.controls, event.previousIndex, event.currentIndex); + fieldssFormArray.updateValueAndValidity(); + } + + + // + // + // externalApiConfig + // + // + addExternalApiConfig(): void { + const externalApiConfig: ReferenceTypeExternalApiConfigurationEditorModel = new ReferenceTypeExternalApiConfigurationEditorModel(); + (this.formGroup.get('definition').get('externalApiConfig') as FormArray).push(externalApiConfig.buildForm()); + this.addFieldMapping(0); + } + + removeExternalApiConfig(externalApiConfigIndex: number): void { + (this.formGroup.get('definition').get('externalApiConfig') as FormArray).removeAt(externalApiConfigIndex); + } + + dropExternalApiConfig(event: CdkDragDrop) { + const fieldssFormArray = (this.formGroup.get('definition').get('externalApiConfig') as FormArray); + + moveItemInArray(fieldssFormArray.controls, event.previousIndex, event.currentIndex); + fieldssFormArray.updateValueAndValidity(); + } + + // + // + // resultFieldsMapping + // + // + addFieldMapping(externalApiConfigIndex: number): void { + const fieldMapping: ResultFieldsMappingConfigurationEditorModel = new ResultFieldsMappingConfigurationEditorModel(); + ((this.formGroup.get('definition').get('externalApiConfig') as FormArray).at(externalApiConfigIndex).get('results').get('fieldsMapping') as FormArray).push(fieldMapping.buildForm()); + } + + removeFieldMapping(externalApiConfigIndex: number, fieldMappingIndex: number): void { + const formArray = ((this.formGroup.get('definition').get('externalApiConfig') as FormArray).at(externalApiConfigIndex) as FormArray); + (formArray.get('results').get('fieldsMapping') as FormArray).removeAt(fieldMappingIndex); + } + + dropFieldsMapping(event: CdkDragDrop) { + const fieldssFormArray = (this.formGroup.get('definition').get('externalApiConfig').get('fieldsMapping') as FormArray); + + moveItemInArray(fieldssFormArray.controls, event.previousIndex, event.currentIndex); + fieldssFormArray.updateValueAndValidity(); + } + + + // + // + // queries + // + // + addQuery(externalApiConfigIndex: number): void { + const query: QueryConfigEditorModel = new QueryConfigEditorModel(); + ((this.formGroup.get('definition').get('externalApiConfig') as FormArray).at(externalApiConfigIndex).get('queries') as FormArray).push(query.buildForm()); + } + + removeQuery(externalApiConfigIndex: number, fieldMappingIndex: number): void { + const formArray = ((this.formGroup.get('definition').get('externalApiConfig') as FormArray).at(externalApiConfigIndex).get('queries') as FormArray); + formArray.removeAt(fieldMappingIndex); + } + + dropQueries(event: CdkDragDrop) { + const fieldssFormArray = (this.formGroup.get('definition').get('externalApiConfig').get('queries') as FormArray); + + moveItemInArray(fieldssFormArray.controls, event.previousIndex, event.currentIndex); + fieldssFormArray.updateValueAndValidity(); + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts new file mode 100644 index 000000000..46a67d689 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.model.ts @@ -0,0 +1,525 @@ +import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms"; +import { ReferenceFieldDataType } from "@app/core/common/enum/reference-field-data-type"; +import { ReferenceType, ReferenceTypeDefinition, ReferenceTypeDefinitionPersist, ReferenceTypePersist, ReferenceTypeFieldPersist,ReferenceTypeField, AuthenticationConfiguration, AuthenticationConfigurationPersist, QueryConfigPersist, QueryConfig, ReferenceTypeExternalApiConfigurationPersist, ResultsConfigurationPersist, ResultFieldsMappingConfigurationPersist, ReferenceTypeExternalApiConfiguration, ResultsConfiguration, ResultFieldsMappingConfiguration } from "@app/core/model/reference-type/reference-type"; +import { BaseEditorModel } from "@common/base/base-form-editor-model"; +import { BackendErrorValidator } from "@common/forms/validation/custom-validator"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; +import { Validation, ValidationContext } from "@common/forms/validation/validation-context"; + +export class ReferenceTypeEditorModel extends BaseEditorModel implements ReferenceTypePersist { + name: string; + code: string; + definition: ReferenceTypeDefinitionEditorModel = new ReferenceTypeDefinitionEditorModel(); + + permissions: string[]; + + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel(); + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor() { super(); } + + public fromModel(item: ReferenceType): ReferenceTypeEditorModel { + if (item) { + super.fromModel(item); + this.name = item.name; + this.code = item.code; + this.definition = new ReferenceTypeDefinitionEditorModel().fromModel(item.definition); + } + return this; + } + + buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup { + if (context == null) { context = this.createValidationContext(); } + + return this.formBuilder.group({ + id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators], + name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators], + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + definition: this.definition.buildForm({ + rootPath: `definition.` + }) + }); + } + + createValidationContext(): ValidationContext { + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] }); + baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'name')] }); + baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'code')] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class ReferenceTypeDefinitionEditorModel implements ReferenceTypeDefinitionPersist { + fields: ReferenceTypeFieldEditorModel[] = []; + externalApiConfig: ReferenceTypeExternalApiConfigurationEditorModel[] = []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: ReferenceTypeDefinition): ReferenceTypeDefinitionEditorModel { + if (item) { + if (item.fields) { item.fields.map(x => this.fields.push(new ReferenceTypeFieldEditorModel().fromModel(x))); } + if(item.externalApiConfig) { item.externalApiConfig.map(x => this.externalApiConfig.push(new ReferenceTypeExternalApiConfigurationEditorModel().fromModel(x))); } + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ReferenceTypeDefinitionEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + fields: this.formBuilder.array( + (this.fields ?? []).map( + (item, index) => new ReferenceTypeFieldEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `fields[${index}].` + }), context.getValidation('fields') + ) + ), + externalApiConfig: this.formBuilder.array( + (this.externalApiConfig ?? []).map( + (item, index) => new ReferenceTypeExternalApiConfigurationEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `externalApiConfig[${index}].` + }), context.getValidation('externalApiConfig') + ) + ) + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'fields', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fields`)] }); + baseValidationArray.push({ key: 'externalApiConfig', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}externalApiConfig`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + +} + +export class ReferenceTypeFieldEditorModel implements ReferenceTypeFieldPersist { + code: string; + dataType: ReferenceFieldDataType; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + public fromModel(item: ReferenceTypeField): ReferenceTypeFieldEditorModel { + if (item) { + this.code = item.code; + this.dataType = item.dataType; + } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ReferenceTypeFieldEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + dataType: [{ value: this.dataType, disabled: disabled }, context.getValidation('dataType').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'dataType', validators: [Validators.required,BackendErrorValidator(validationErrorModel, `${rootPath}dataType`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } + +} + +export class ReferenceTypeExternalApiConfigurationEditorModel implements ReferenceTypeExternalApiConfigurationPersist { + public key: string; + public label: string; + public ordinal: number; + public url: string; + public results: ResultsConfigurationEditorModel = new ResultsConfigurationEditorModel(); + public type: string; + public paginationPath: string; + public contentType: string; + public firstPage: string; + public requestType?: string; + public requestBody?: string; + public filterType?: string; + public auth: AuthenticationConfigurationEditorModel = new AuthenticationConfigurationEditorModel(); + public queries?: QueryConfigEditorModel[] = []; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: ReferenceTypeExternalApiConfiguration): ReferenceTypeExternalApiConfigurationEditorModel { + this.key = item.key; + this.label = item.label; + this.ordinal = item.ordinal; + this.url = item.url; + this.results = new ResultsConfigurationEditorModel().fromModel(item.results); + this.type = item.type; + this.paginationPath = item.paginationPath; + this.contentType = item.contentType; + this.firstPage = item.firstPage; + this.requestType = item.requestType; + this.requestBody = item.requestBody; + this.filterType = item.filterType; + this.auth = new AuthenticationConfigurationEditorModel().fromModel(item.auth); + if(item.queries) { item.queries.map(x => this.queries.push(new QueryConfigEditorModel().fromModel(x))); } + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ReferenceTypeExternalApiConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + + return this.formBuilder.group({ + key: [{ value: this.key, disabled: disabled }, context.getValidation('key').validators], + label: [{ value: this.label, disabled: disabled }, context.getValidation('label').validators], + ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal').validators], + url: [{ value: this.url, disabled: disabled }, context.getValidation('url').validators], + results: this.results.buildForm({ + rootPath: `results.` + }), + type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], + paginationPath: [{ value: this.paginationPath, disabled: disabled }, context.getValidation('paginationPath').validators], + contentType: [{ value: this.contentType, disabled: disabled }, context.getValidation('contentType').validators], + firstPage: [{ value: this.firstPage, disabled: disabled }, context.getValidation('firstPage').validators], + requestType: [{ value: this.requestType, disabled: disabled }, context.getValidation('requestType').validators], + requestBody: [{ value: this.requestBody, disabled: disabled }, context.getValidation('requestBody').validators], + filterType: [{ value: this.filterType, disabled: disabled }, context.getValidation('filterType').validators], + auth: this.auth.buildForm({ + rootPath: `auth.` + }), + queries: this.formBuilder.array( + (this.queries ?? []).map( + (item, index) => new QueryConfigEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `queries[${index}].` + }), context.getValidation('queries') + ) + ) + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'key', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}key`)] }); + baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}label`)] }); + baseValidationArray.push({ key: 'ordinal', validators: [Validators.required, Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}ordinal`)] }); + baseValidationArray.push({ key: 'url', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}url`)] }); + baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); + baseValidationArray.push({ key: 'paginationPath', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}paginationPath`)] }); + baseValidationArray.push({ key: 'contentType', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}contentType`)] }); + baseValidationArray.push({ key: 'firstPage', validators: [Validators.required, Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}firstPage`)] }); + baseValidationArray.push({ key: 'requestType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}requestType`)] }); + baseValidationArray.push({ key: 'requestBody', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}requestBody`)] }); + baseValidationArray.push({ key: 'filterType', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}filterType`)] }); + baseValidationArray.push({ key: 'queries', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}queries`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class ResultsConfigurationEditorModel implements ResultsConfigurationPersist { + public resultsArrayPath: string; + public fieldsMapping: ResultFieldsMappingConfigurationEditorModel[] =[]; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: ResultsConfiguration): ResultsConfigurationEditorModel { + this.resultsArrayPath = item.resultsArrayPath; + if(item.fieldsMapping) { item.fieldsMapping.map(x => this.fieldsMapping.push(new ResultFieldsMappingConfigurationEditorModel().fromModel(x))); } + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ResultsConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + resultsArrayPath: [{ value: this.resultsArrayPath, disabled: disabled }, context.getValidation('resultsArrayPath').validators], + fieldsMapping: this.formBuilder.array( + (this.fieldsMapping ?? []).map( + (item, index) => new ResultFieldsMappingConfigurationEditorModel( + this.validationErrorModel + ).fromModel(item).buildForm({ + rootPath: `fieldsMapping[${index}].` + }), context.getValidation('fieldsMapping') + ) + ) + + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'resultsArrayPath', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}resultsArrayPath`)] }); + baseValidationArray.push({ key: 'fieldsMapping', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fieldsMapping`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class ResultFieldsMappingConfigurationEditorModel implements ResultFieldsMappingConfigurationPersist { + public code: string; + public responsePath: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: ResultFieldsMappingConfiguration): ResultFieldsMappingConfigurationEditorModel { + this.code = item.code; + this.responsePath = item.responsePath; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = ResultFieldsMappingConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + code: [{ value: this.code, disabled: disabled }, context.getValidation('code').validators], + responsePath: [{ value: this.responsePath, disabled: disabled }, context.getValidation('responsePath').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'code', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}code`)] }); + baseValidationArray.push({ key: 'responsePath', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}responsePath`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class AuthenticationConfigurationEditorModel implements AuthenticationConfigurationPersist { + public authUrl: string; + public authMethod: string; + public authTokenPath: string; + public authRequestBody: string; + public type: string; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: AuthenticationConfiguration): AuthenticationConfigurationEditorModel { + this.authUrl = item.authUrl; + this.authMethod = item.authMethod; + this.authTokenPath = item.authTokenPath; + this.authRequestBody = item.authRequestBody; + this.type = item.type; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = AuthenticationConfigurationEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + authUrl: [{ value: this.authUrl, disabled: disabled }, context.getValidation('authUrl').validators], + authMethod: [{ value: this.authMethod, disabled: disabled }, context.getValidation('authMethod').validators], + authTokenPath: [{ value: this.authTokenPath, disabled: disabled }, context.getValidation('authTokenPath').validators], + authRequestBody: [{ value: this.authRequestBody, disabled: disabled }, context.getValidation('authRequestBody').validators], + type: [{ value: this.type, disabled: disabled }, context.getValidation('type').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'authUrl', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}authUrl`)] }); + baseValidationArray.push({ key: 'authMethod', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}authMethod`)] }); + baseValidationArray.push({ key: 'authTokenPath', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}authTokenPath`)] }); + baseValidationArray.push({ key: 'authRequestBody', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}authRequestBody`)] }); + baseValidationArray.push({ key: 'type', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}type`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} + +export class QueryConfigEditorModel implements QueryConfigPersist { + public condition: string; + public separator: string; + public value: string; + public ordinal: number; + + protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder(); + + constructor( + public validationErrorModel: ValidationErrorModel = new ValidationErrorModel() + ) { } + + fromModel(item: QueryConfig): QueryConfigEditorModel { + this.condition = item.condition; + this.separator = item.separator; + this.value = item.value; + this.ordinal = item.ordinal; + + return this; + } + + buildForm(params?: { + context?: ValidationContext, + disabled?: boolean, + rootPath?: string + }): UntypedFormGroup { + let { context = null, disabled = false, rootPath } = params ?? {} + if (context == null) { + context = QueryConfigEditorModel.createValidationContext({ + validationErrorModel: this.validationErrorModel, + rootPath + }); + } + + return this.formBuilder.group({ + condition: [{ value: this.condition, disabled: disabled }, context.getValidation('condition').validators], + separator: [{ value: this.separator, disabled: disabled }, context.getValidation('separator').validators], + value: [{ value: this.value, disabled: disabled }, context.getValidation('value').validators], + ordinal: [{ value: this.ordinal, disabled: disabled }, context.getValidation('ordinal').validators], + }); + } + + static createValidationContext(params: { + rootPath?: string, + validationErrorModel: ValidationErrorModel + }): ValidationContext { + const { rootPath = '', validationErrorModel } = params; + + const baseContext: ValidationContext = new ValidationContext(); + const baseValidationArray: Validation[] = new Array(); + baseValidationArray.push({ key: 'condition', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}condition`)] }); + baseValidationArray.push({ key: 'separator', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}separator`)] }); + baseValidationArray.push({ key: 'value', validators: [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}value`)] }); + baseValidationArray.push({ key: 'ordinal', validators: [Validators.required, Validators.pattern("^[0-9]*$"), BackendErrorValidator(validationErrorModel, `${rootPath}ordinal`)] }); + + baseContext.validation = baseValidationArray; + return baseContext; + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts new file mode 100644 index 000000000..9f5854885 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.resolver.ts @@ -0,0 +1,77 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { AuthenticationConfiguration, QueryConfig, ReferenceType, ReferenceTypeDefinition, ReferenceTypeField, ReferenceTypeExternalApiConfiguration, ResultsConfiguration, ResultFieldsMappingConfiguration } from '@app/core/model/reference-type/reference-type'; +import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { BaseEditorResolver } from '@common/base/base-editor.resolver'; +import { Guid } from '@common/types/guid'; +import { takeUntil, tap } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Injectable() +export class ReferenceTypeEditorResolver extends BaseEditorResolver { + + constructor(private ReferenceTypeService: ReferenceTypeService, private breadcrumbService: BreadcrumbService) { + super(); + } + + public static lookupFields(): string[] { + return [ + ...BaseEditorResolver.lookupFields(), + nameof(x => x.id), + nameof(x => x.name), + nameof(x => x.code), + + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.fields), nameof(x => x.dataType)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.key)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.label)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.ordinal)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.url)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results), nameof(x => x.resultsArrayPath)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.code)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results), nameof(x => x.fieldsMapping), nameof(x => x.responsePath)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.type)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.paginationPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.contentType)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.firstPage)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.requestType)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.requestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.filterType)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.authUrl)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.authMethod)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.authTokenPath)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.authRequestBody)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.type)].join('.'), + + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.condition)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.separator)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.value)].join('.'), + [nameof(x => x.definition), nameof(x => x.externalApiConfig), nameof(x => x.results),nameof(x => x.ordinal)].join('.'), + + nameof(x => x.createdAt), + nameof(x => x.updatedAt), + nameof(x => x.isActive) + ] + } + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + + const fields = [ + ...ReferenceTypeEditorResolver.lookupFields() + ]; + const id = route.paramMap.get('id'); + //const cloneid = route.paramMap.get('cloneid'); + if (id != null) { + return this.ReferenceTypeService.getSingle(Guid.parse(id), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.code)), takeUntil(this._destroyed)); + } + // } else if (cloneid != null) { + // return this.ReferenceTypeService.clone(Guid.parse(cloneid), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed)); + // } + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.service.ts b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.service.ts new file mode 100644 index 000000000..e7f779057 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/editor/reference-type-editor.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@angular/core"; +import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model"; + +@Injectable() +export class ReferenceTypeEditorService { + private validationErrorModel: ValidationErrorModel; + + public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void { + this.validationErrorModel = validationErrorModel; + } + + public getValidationErrorModel(): ValidationErrorModel { + return this.validationErrorModel; + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.html b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.html new file mode 100644 index 000000000..ff9454bac --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.html @@ -0,0 +1,36 @@ +
+ + + + + +
+
+
+

{{'REFERENCE-TYPE-LISTING.FILTER.TITLE' | translate}}

+ +
+ + + {{'REFERENCE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}} + + +
+ + +
+
+
+
+ + +
diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.scss b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.scss new file mode 100644 index 000000000..999f5a7c6 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.scss @@ -0,0 +1,25 @@ +.description-template-type-listing-filters { + +} + +::ng-deep.mat-mdc-menu-panel { + max-width: 100% !important; + height: 100% !important; +} + +:host::ng-deep.mat-mdc-menu-content:not(:empty) { + padding-top: 0 !important; +} + + +.filter-button{ + padding-top: .6rem; + padding-bottom: .6rem; + // .mat-icon{ + // font-size: 1.5em; + // width: 1.2em; + // height: 1.2em; + // } +} + + diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.ts b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.ts new file mode 100644 index 000000000..5a0cf7f66 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/filters/reference-type-listing-filters.component.ts @@ -0,0 +1,94 @@ +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { ReferenceTypeFilter } from '@app/core/query/reference-type.lookup'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { BaseComponent } from '@common/base/base.component'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + selector: 'app-reference-type-listing-filters', + templateUrl: './reference-type-listing-filters.component.html', + styleUrls: ['./reference-type-listing-filters.component.scss'] +}) +export class ReferenceTypeListingFiltersComponent extends BaseComponent implements OnInit, OnChanges { + + @Input() readonly filter: ReferenceTypeFilter; + @Output() filterChange = new EventEmitter(); + + // * State + internalFilters: ReferenceTypeListingFilters = this._getEmptyFilters(); + + protected appliedFilterCount: number = 0; + constructor( + public enumUtils: EnumUtils, + ) { super(); } + + ngOnInit() { + } + + ngOnChanges(changes: SimpleChanges): void { + const filterChange = changes[nameof(x => x.filter)]?.currentValue as ReferenceTypeFilter; + if (filterChange) { + this.updateFilters() + } + } + + + onSearchTermChange(searchTerm: string): void { + this.applyFilters() + } + + + protected updateFilters(): void { + this.internalFilters = this._parseToInternalFilters(this.filter); + this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters); + } + + protected applyFilters(): void { + const { isActive, like } = this.internalFilters ?? {} + this.filterChange.emit({ + ...this.filter, + like, + isActive: isActive ? [IsActive.Active] : [IsActive.Inactive] + }) + } + + + private _parseToInternalFilters(inputFilter: ReferenceTypeFilter): ReferenceTypeListingFilters { + if (!inputFilter) { + return this._getEmptyFilters(); + } + + let { excludedIds, ids, isActive, like } = inputFilter; + + return { + isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length, + like: like + } + + } + + private _getEmptyFilters(): ReferenceTypeListingFilters { + return { + isActive: true, + like: null, + } + } + + private _computeAppliedFilters(filters: ReferenceTypeListingFilters): number { + let count = 0; + if (filters?.isActive) { + count++ + } + return count; + } + + clearFilters() { + this.internalFilters = this._getEmptyFilters(); + } +} + +interface ReferenceTypeListingFilters { + isActive: boolean; + like: string; +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html new file mode 100644 index 000000000..0c2430644 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.html @@ -0,0 +1,108 @@ +
+
+ +
+
+

{{'REFERENCE-TYPE-LISTING.TITLE' | translate}}

+ + +
+
+ +
+
+ + + + + + + + + +
+
+ + + + +
+
+ + {{item?.name | nullifyValue}} +
+
+ + +
+
+ {{enumUtils.toDescriptionTemplateTypeStatusString(item.status) | nullifyValue}} +
+
+
+ + + + {{'REFERENCE-TYPE-LISTING.FIELDS.CREATED-AT' | translate}}: + + {{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}} + + +
+
+ + + {{'REFERENCE-TYPE-LISTING.FIELDS.UPDATED-AT' | translate}}: + + {{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}} + + + +
+
+
+ + + + +
+
+ + + + + + +
+
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss new file mode 100644 index 000000000..6e1b48814 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.scss @@ -0,0 +1,60 @@ +.description-template-type-listing { + margin-top: 1.3rem; + margin-left: 1rem; + margin-right: 2rem; + + .mat-header-row{ + background: #f3f5f8; + } + .mat-card { + margin: 16px 0; + padding: 0px; + } + + .mat-row { + cursor: pointer; + min-height: 4.5em; + } + + mat-row:hover { + background-color: #eef5f6; + } + .mat-fab-bottom-right { + float: right; + z-index: 5; + } +} +.create-btn { + border-radius: 30px; + background-color: var(--secondary-color); + padding-left: 2em; + padding-right: 2em; + // color: #000; + + .button-text{ + display: inline-block; + } +} + +.dlt-btn { + color: rgba(0, 0, 0, 0.54); +} + +.status-chip{ + + border-radius: 20px; + padding-left: 1em; + padding-right: 1em; + padding-top: 0.2em; + font-size: .8em; +} + +.status-chip-finalized{ + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; +} + +.status-chip-draft{ + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.ts b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.ts new file mode 100644 index 000000000..cd9a4fda0 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/listing/reference-type-listing.component.ts @@ -0,0 +1,171 @@ +import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { IsActive } from '@app/core/common/enum/is-active.enum'; +import { ReferenceType } from '@app/core/model/reference-type/reference-type'; +import { ReferenceTypeLookup } from '@app/core/query/reference-type.lookup'; +import { AuthService } from '@app/core/services/auth/auth.service'; +import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { QueryParamsService } from '@app/core/services/utilities/query-params.service'; +import { BaseListingComponent } from '@common/base/base-listing-component'; +import { PipeService } from '@common/formatting/pipe.service'; +import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe'; +import { QueryResult } from '@common/model/query-result'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service'; +import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component'; +import { Guid } from '@common/types/guid'; +import { TranslateService } from '@ngx-translate/core'; +import { Observable } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { nameof } from 'ts-simple-nameof'; + +@Component({ + templateUrl: './reference-type-listing.component.html', + styleUrls: ['./reference-type-listing.component.scss'] +}) +export class ReferenceTypeListingComponent extends BaseListingComponent implements OnInit { + publish = false; + userSettingsKey = { key: 'ReferenceTypeListingUserSettings' }; + propertiesAvailableForOrder: ColumnDefinition[]; + //ReferenceTypeStatuses = ReferenceTypeStatus; + + //@ViewChild('ReferenceTypeStatus', { static: true }) ReferenceTypeStatus?: TemplateRef; + @ViewChild('actions', { static: true }) actions?: TemplateRef; + @ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent; + + private readonly lookupFields: string[] = [ + nameof(x => x.id), + nameof(x => x.name), + nameof(x => x.code), + nameof(x => x.updatedAt), + nameof(x => x.createdAt), + nameof(x => x.isActive) + ]; + + rowIdentity = x => x.id; + + constructor( + protected router: Router, + protected route: ActivatedRoute, + protected uiNotificationService: UiNotificationService, + protected httpErrorHandlingService: HttpErrorHandlingService, + protected queryParamsService: QueryParamsService, + private referenceTypeService: ReferenceTypeService, + public authService: AuthService, + private pipeService: PipeService, + public enumUtils: EnumUtils, + private language: TranslateService, + private dialog: MatDialog + ) { + super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService); + // Lookup setup + // Default lookup values are defined in the user settings class. + this.lookup = this.initializeLookup(); + } + + ngOnInit() { + super.ngOnInit(); + } + + protected initializeLookup(): ReferenceTypeLookup { + const lookup = new ReferenceTypeLookup(); + lookup.metadata = { countAll: true }; + lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE }; + lookup.isActive = [IsActive.Active]; + lookup.order = { items: [this.toDescSortField(nameof(x => x.createdAt))] }; + this.updateOrderUiFields(lookup.order); + + lookup.project = { + fields: this.lookupFields + }; + + return lookup; + } + + protected setupColumns() { + this.gridColumns.push(...[{ + prop: nameof(x => x.name), + sortable: true, + languageName: 'REFERENCE-TYPE-LISTING.FIELDS.NAME' + }, + { + prop: nameof(x => x.code), + sortable: true, + languageName: 'REFERENCE-TYPE-LISTING.FIELDS.CODE', + //cellTemplate: this.ReferenceTypeStatus + }, + { + prop: nameof(x => x.createdAt), + sortable: true, + languageName: 'REFERENCE-TYPE-LISTING.FIELDS.CREATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + prop: nameof(x => x.updatedAt), + sortable: true, + languageName: 'REFERENCE-TYPE-LISTING.FIELDS.UPDATED-AT', + pipe: this.pipeService.getPipe(DataTableDateTimeFormatPipe).withFormat('short') + }, + { + alwaysShown: true, + cellTemplate: this.actions, + maxWidth: 120 + } + ]); + this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable); + } + + // + // Listing Component functions + // + onColumnsChanged(event: ColumnsChangedEvent) { + super.onColumnsChanged(event); + this.onColumnsChangedInternal(event.properties.map(x => x.toString())); + } + + private onColumnsChangedInternal(columns: string[]) { + // Here are defined the projection fields that always requested from the api. + const fields = new Set(this.lookupFields); + this.gridColumns.map(x => x.prop) + .filter(x => !columns?.includes(x as string)) + .forEach(item => { + fields.delete(item as string) + }); + this.lookup.project = { fields: [...fields] }; + this.onPageLoad({ offset: 0 } as PageLoadEvent); + } + + protected loadListing(): Observable> { + return this.referenceTypeService.query(this.lookup); + } + + public deleteType(id: Guid) { + if (id) { + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + data: { + isDeleteConfirmation: true, + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL') + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this.referenceTypeService.delete(id).pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + }); + } + } + + onCallbackSuccess(): void { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success); + this.ngOnInit(); + } +} diff --git a/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts new file mode 100644 index 000000000..8a4e8ab4d --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.module.ts @@ -0,0 +1,44 @@ +import { NgModule } from '@angular/core'; +import { EditorModule } from '@tinymce/tinymce-angular'; +import { ReferenceTypeRoutingModule } from './reference-type.routing'; +import { CommonUiModule } from '@common/ui/common-ui.module'; +import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { ReferenceTypeEditorComponent } from './editor/reference-type-editor.component'; +import { CommonModule } from '@angular/common'; +import { HybridListingModule } from '@common/modules/hybrid-listing/hybrid-listing.module'; +import { TextFilterModule } from '@common/modules/text-filter/text-filter.module'; +import { UserSettingsModule } from '@common/modules/user-settings/user-settings.module'; +import { ReferenceTypeListingFiltersComponent } from './listing/filters/reference-type-listing-filters.component'; +import { UrlListingModule } from '@app/library/url-listing/url-listing.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; +import { NgxDropzoneModule } from 'ngx-dropzone'; +import { DragDropModule } from '@angular/cdk/drag-drop'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; +import { CommonFormattingModule } from '@common/formatting/common-formatting.module'; +import { ReferenceTypeListingComponent } from './listing/reference-type-listing.component'; + + +@NgModule({ + declarations: [ + ReferenceTypeEditorComponent, + ReferenceTypeListingComponent, + ReferenceTypeListingFiltersComponent +], + imports: [ + CommonModule, + CommonUiModule, + CommonFormsModule, + UrlListingModule, + ConfirmationDialogModule, + ReferenceTypeRoutingModule, + EditorModule, + HybridListingModule, + TextFilterModule, + UserSettingsModule, + NgxDropzoneModule, + DragDropModule, + AutoCompleteModule, + CommonFormattingModule + ] +}) +export class ReferenceTypeModule { } diff --git a/dmp-frontend/src/app/ui/admin/reference-type/reference-type.routing.ts b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.routing.ts new file mode 100644 index 000000000..d50a8b00a --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/reference-type/reference-type.routing.ts @@ -0,0 +1,58 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { AdminAuthGuard } from '@app/core/admin-auth-guard.service'; +import { ReferenceTypeEditorComponent } from './editor/reference-type-editor.component'; +import { ReferenceTypeListingComponent } from './listing/reference-type-listing.component'; +import { AuthGuard } from '@app/core/auth-guard.service'; +import { AppPermission } from '@app/core/common/enum/permission.enum'; +import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service'; +import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service'; +import { ReferenceTypeEditorResolver } from './editor/reference-type-editor.resolver'; + + +const routes: Routes = [ + { + path: '', + component: ReferenceTypeListingComponent, + canActivate: [AuthGuard] + }, + { + path: 'new', + component: ReferenceTypeEditorComponent, + canActivate: [AdminAuthGuard], + data: { + authContext: { + permissions: [AppPermission.EditReferenceType] + }, + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.NEW-DESCRIPTION-TEMPLATE-TYPE' + }) + } + }, + { + path: ':id', + canActivate: [AuthGuard], + component: ReferenceTypeEditorComponent, + canDeactivate: [PendingChangesGuard], + resolve: { + 'entity': ReferenceTypeEditorResolver + }, + data: { + ...BreadcrumbService.generateRouteDataConfiguration({ + title: 'BREADCRUMBS.EDIT-DESCRIPTION-TEMPLATE-TYPE' + }), + authContext: { + permissions: [AppPermission.EditReferenceType] + } + } + + }, + { path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ReferenceTypeEditorResolver] +}) +export class ReferenceTypeRoutingModule { } diff --git a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts index a75c8a90d..0c0f9b6d3 100644 --- a/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts +++ b/dmp-frontend/src/app/ui/sidebar/sidebar.component.ts @@ -53,6 +53,7 @@ export const ADMIN_ROUTES: RouteInfo[] = [ { path: '/dmp-blueprints', title: 'SIDE-BAR.DMP-BLUEPRINTS', icon: 'library_books' }, { path: '/description-templates', title: 'SIDE-BAR.DESCRIPTION-TEMPLATES', icon: 'library_books' }, { path: '/description-template-type', title: 'SIDE-BAR.DESCRIPTION-TEMPLATE-TYPES', icon: 'library_books' }, + { path: '/reference-type', title: 'SIDE-BAR.REFERENCE-TYPES', icon: 'library_books' }, { path: '/users', title: 'SIDE-BAR.USERS', icon: 'people' }, { path: '/language-editor', title: 'SIDE-BAR.LANGUAGE-EDITOR', icon: 'language' }, { path: '/supportive-material', title: 'SIDE-BAR.SUPPORTIVE-MATERIAL', icon: 'import_contacts' } diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index c3d15a006..d90ca7e83 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -171,7 +171,8 @@ "LANGUAGE-EDITOR": "Language Editor", "GUIDE-EDITOR": "User Guide Editor", "LANGUAGE": "Language", - "SIGN-IN": "Sign in to account" + "SIGN-IN": "Sign in to account", + "REFERENCE-TYPE": "Reference Types" }, "FILE-TYPES": { "PDF": "PDF", @@ -332,7 +333,8 @@ "CO-BRANDING": "Co-Branding", "SUPPORT": "Support", "FEEDBACK": "Send feedback", - "SUPPORTIVE-MATERIAL": "Supportive Material" + "SUPPORTIVE-MATERIAL": "Supportive Material", + "REFERENCE-TYPES":"Reference Types" }, "DATASET-PROFILE-EDITOR": { "TITLE": { @@ -1047,6 +1049,33 @@ "SUCCESSFUL-DELETE": "Successful Delete", "UNSUCCESSFUL-DELETE": "This item could not be deleted." }, + "REFERENCE-TYPE-LISTING": { + "TITLE": "Reference Types", + "CREATE-TYPE": "Create Reference Type", + "FIELDS": { + "NAME": "Name", + "CODE": "Code", + "UPDATED-AT": "Updated", + "CREATED-AT": "Created" + }, + "FILTER": { + "TITLE": "Filters", + "IS-ACTIVE": "Is Active", + "CANCEL": "Cancel", + "APPLY-FILTERS": "Apply filters" + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Reference Type?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + }, + "ACTIONS": { + "DELETE": "Delete", + "EDIT": "Edit" + }, + "SUCCESSFUL-DELETE": "Successful Delete", + "UNSUCCESSFUL-DELETE": "This item could not be deleted." + }, "DATASET-UPLOAD": { "TITLE": "Import Dataset", "UPLOAD-BUTTON": "Upload", @@ -1075,6 +1104,39 @@ "DELETE": "Delete" } }, + "REFERENCE-TYPE-EDITOR": { + "NEW": "New Reference Type", + "FIELDS": { + "EXTERNAL-API-CONFIGURATION": "External API Configuration", + "RESULTS": "Results", + "FIELD-MAPPING": "Field Mapping", + "AUTHENTICATION": "Authentication", + "QUERIES": "Queries", + "QUERY": "Query", + "NAME": "Name", + "CODE": "Code", + "FIELD": "Field", + "DATA-TYPE": "Data Type", + "KEY": "Key", + "LABEL": "Label", + "ORDINAL": "Ordinal", + "URL": "Url", + "PAGINATION-PATH": "Pagination Path" + }, + "ACTIONS": { + "SAVE": "Save", + "CANCEL": "Cancel", + "DELETE": "Delete", + "ADD-FIELD": "Add Field", + "ADD-CONFIG": "Add Config", + "ADD-QUERY": "Add Query" + } + }, + "CONFIRM-DELETE-DIALOG": { + "MESSAGE": "Would you like to delete this Reference type?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" + }, "DMP-BLUEPRINT-EDITOR": { "TITLE": { "NEW": "New DMP Blueprint",