vocabulary terms

This commit is contained in:
Michele Artini 2023-01-30 12:37:01 +01:00
parent 527a80c931
commit e83bd36fc1
7 changed files with 236 additions and 37 deletions

View File

@ -29,7 +29,7 @@ import { MatSortModule } from '@angular/material/sort';
import { ResourcesComponent, ResContentDialog, ResCreateNewDialog, ResMetadataDialog } from './resources/resources.component' import { ResourcesComponent, ResContentDialog, ResCreateNewDialog, ResMetadataDialog } from './resources/resources.component'
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { ContextsComponent, ContextViewerComponent, ContextParamsDialog } from './contexts/contexts.component'; import { ContextsComponent, ContextViewerComponent, ContextParamsDialog } from './contexts/contexts.component';
import { VocabulariesComponent, VocabularyEditorComponent, VocDialog } from './vocabularies/vocabularies.component'; import { VocabulariesComponent, VocabularyEditorComponent, VocDialog, VocTermDialog } from './vocabularies/vocabularies.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
@ -50,7 +50,8 @@ import { VocabulariesComponent, VocabularyEditorComponent, VocDialog } from './v
ContextParamsDialog, ContextParamsDialog,
VocabulariesComponent, VocabulariesComponent,
VocabularyEditorComponent, VocabularyEditorComponent,
VocDialog VocDialog,
VocTermDialog
], ],
imports: [ imports: [
BrowserModule, BrowserModule,

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { ResourceType, Protocol, WfHistoryEntry, SimpleResource, Context, ContextNode, Vocabulary } from './model/controller.model'; import { ResourceType, Protocol, WfHistoryEntry, SimpleResource, Context, ContextNode, Vocabulary, VocabularyTerm } from './model/controller.model';
import { Observable, Observer } from 'rxjs'; import { Observable, Observer } from 'rxjs';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
@ -140,6 +140,20 @@ export class ISService {
}); });
} }
loadVocabulary(vocId:string, onSuccess: Function): void {
this.client.get<Vocabulary>('./ajax/vocs/' + encodeURIComponent(vocId)).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
loadVocabularyTerms(vocId:string, onSuccess: Function): void {
this.client.get<VocabularyTerm[]>('./ajax/vocs/' + encodeURIComponent(vocId) + '/terms').subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
saveVocabulary(voc:Vocabulary, onSuccess: Function, relatedForm?: FormGroup): void { saveVocabulary(voc:Vocabulary, onSuccess: Function, relatedForm?: FormGroup): void {
this.client.post<void>('./ajax/vocs/', voc).subscribe({ this.client.post<void>('./ajax/vocs/', voc).subscribe({
next: data => onSuccess(data), next: data => onSuccess(data),
@ -147,6 +161,13 @@ export class ISService {
}); });
} }
saveVocabularyTerm(vocId:string, term:VocabularyTerm, onSuccess: Function, relatedForm?: FormGroup): void {
this.client.post<void>('./ajax/vocs/' + encodeURIComponent(vocId) + '/terms', term).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error, relatedForm)
});
}
deleteVocabulary(vocId:string, onSuccess: Function): void { deleteVocabulary(vocId:string, onSuccess: Function): void {
this.client.delete<void>('./ajax/vocs/' + encodeURIComponent(vocId)).subscribe({ this.client.delete<void>('./ajax/vocs/' + encodeURIComponent(vocId)).subscribe({
next: data => onSuccess(data), next: data => onSuccess(data),
@ -154,6 +175,17 @@ export class ISService {
}); });
} }
deleteVocabularyTerm(vocId:string, termCode:string, onSuccess: Function): void {
this.client.delete<void>('./ajax/vocs/'
+ encodeURIComponent(vocId)
+ '/terms/'
+ encodeURIComponent(termCode)
).subscribe({
next: data => onSuccess(data),
error: error => this.showError(error)
});
}
private showError(error: any, form?: FormGroup) { private showError(error: any, form?: FormGroup) {
const msg = this.errorMessage(error); const msg = this.errorMessage(error);
if (form) { if (form) {

View File

@ -82,3 +82,16 @@ export interface Vocabulary {
name: string, name: string,
description?: string description?: string
} }
export interface VocabularyTermSynonym {
term: string,
encoding: string
}
export interface VocabularyTerm {
code: string,
vocabulary: string,
name: string,
encoding: string,
synonyms: VocabularyTermSynonym[]
}

View File

@ -30,7 +30,7 @@
<th mat-header-cell *matHeaderCellDef style="text-align: right;"></th> <th mat-header-cell *matHeaderCellDef style="text-align: right;"></th>
<td mat-cell *matCellDef="let element" class="table-buttons"> <td mat-cell *matCellDef="let element" class="table-buttons">
<button mat-stroked-button color="primary" (click)="editVocabularyDialog(element)">edit</button> <button mat-stroked-button color="primary" (click)="editVocabularyDialog(element)">edit</button>
<button mat-stroked-button color="warn" (click)="deleteVocabularyDialog(element)">delete</button> <button mat-stroked-button color="warn" (click)="deleteVocabulary(element)">delete</button>
</td> </td>
</ng-container> </ng-container>

View File

@ -7,15 +7,16 @@ import { ActivatedRoute, Params } from '@angular/router';
import { Observable, combineLatest } from 'rxjs'; import { Observable, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatDialog, MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { KeyValue, ContextParam } from '../model/controller.model'; import { VocabularyTerm } from '../model/controller.model';
import { FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; import { FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms';
import { EmitterVisitorContext } from '@angular/compiler';
@Component({ @Component({
selector: 'app-vocabularies', selector: 'app-vocabularies',
templateUrl: './vocabularies.component.html', templateUrl: './vocabularies.component.html',
styleUrls: ['./vocabularies.component.css'] styleUrls: ['./vocabularies.component.css']
}) })
export class VocabulariesComponent { export class VocabulariesComponent implements OnInit, AfterViewInit {
vocsDatasource: MatTableDataSource<Vocabulary> = new MatTableDataSource<Vocabulary>([]); vocsDatasource: MatTableDataSource<Vocabulary> = new MatTableDataSource<Vocabulary>([]);
colums: string[] = ['id', 'name', 'description', 'buttons']; colums: string[] = ['id', 'name', 'description', 'buttons'];
@ -64,13 +65,12 @@ export class VocabulariesComponent {
}); });
} }
deleteVocabularyDialog(vocabulary: Vocabulary): void { deleteVocabulary(vocabulary: Vocabulary): void {
if (confirm("Are you sure?")) { if (confirm("Are you sure?")) {
this.service.deleteVocabulary(vocabulary.id, (data: void) => this.reload()); this.service.deleteVocabulary(vocabulary.id, (data: void) => this.reload());
} }
} }
} }
@Component({ @Component({
@ -78,7 +78,71 @@ export class VocabulariesComponent {
templateUrl: './vocabulary-editor.component.html', templateUrl: './vocabulary-editor.component.html',
styleUrls: ['./vocabularies.component.css'] styleUrls: ['./vocabularies.component.css']
}) })
export class VocabularyEditorComponent { export class VocabularyEditorComponent implements OnInit, AfterViewInit {
voc?: Vocabulary
termsDatasource: MatTableDataSource<VocabularyTerm> = new MatTableDataSource<VocabularyTerm>([]);
colums: string[] = ['code', 'name', 'encoding', 'synonyms', 'buttons'];
@ViewChild(MatSort) sort: MatSort | undefined
constructor(public service: ISService, public route: ActivatedRoute, public dialog: MatDialog) {
}
ngOnInit() {
this.reload();
}
ngAfterViewInit() {
if (this.sort) this.termsDatasource.sort = this.sort;
}
reload() {
this.route.queryParams.subscribe((params) => {
this.service.loadVocabulary(params['id'], (data: Vocabulary) => this.voc = data);
this.service.loadVocabularyTerms(params['id'], (data: VocabularyTerm[]) => this.termsDatasource.data = data);
});
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value.trim().toLowerCase();
this.termsDatasource.filter = filterValue;
}
newVocabularyTermDialog(): void {
if (this.voc?.id) {
const dialogRef = this.dialog.open(VocTermDialog, {
data: { vocabulary: this.voc.id, code: '', name: '', encoding: '', synonyms: []},
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
}
editVocabularyTermDialog(term: VocabularyTerm): void {
const dialogRef = this.dialog.open(VocTermDialog, {
data: term,
width: '80%'
});
dialogRef.afterClosed().subscribe(result => {
if (result) this.reload();
});
}
deleteVocabularyTerm(term: VocabularyTerm): void {
if (confirm("Are you sure?") && this.voc?.id && term.code) {
this.service.deleteVocabularyTerm(this.voc.id, term.code, (data: void) => this.reload());
}
}
} }
@Component({ @Component({
@ -108,3 +172,31 @@ export class VocDialog {
this.dialogRef.close(); this.dialogRef.close();
} }
} }
@Component({
selector: 'voc-term-dialog',
templateUrl: 'voc-term-dialog.html',
styleUrls: ['vocabularies.component.css']
})
export class VocTermDialog {
termForm = new FormGroup({
code: new FormControl(''),
name: new FormControl(''),
encoding: new FormControl('')
});
constructor(public dialogRef: MatDialogRef<VocDialog>, @Inject(MAT_DIALOG_DATA) public data: any, public service: ISService) {
this.termForm.get('code')?.setValue(data.id);
this.termForm.get('name')?.setValue(data.name);
this.termForm.get('encoding')?.setValue(data.encoding);
}
onSubmit(): void {
const term = Object.assign({}, this.data, this.termForm.value);
this.service.saveVocabularyTerm(term.vocabulary, term, (data: void) => this.dialogRef.close(1), this.termForm);
}
onNoClick(): void {
this.dialogRef.close();
}
}

View File

@ -1 +1,61 @@
VOC EDITOR <h2>Vocabulary Editor</h2>
<p>
<b>Vocabulary ID: </b>{{voc?.id}}<br />
<b>Vocabulary Name: </b>{{voc?.name}}<br />
<b>Description: </b>{{voc?.description}}
</p>
<p>
<a mat-stroked-button color="primary" [routerLink]="['/adv_resources/vocabulary']">return to vocabulary list</a>
<button mat-stroked-button color="primary" (click)="newVocabularyTermDialog()">create a new term</button>
<a mat-stroked-button color="primary" href="/api/vocs/{{voc?.id}}/terms" target="_blank">Download</a>
</p>
<mat-form-field style="width: 100%; margin-top: 10px;">
<mat-label>Filter</mat-label>
<input matInput (keyup)="applyFilter($event)" placeholder="Filter..." #input />
</mat-form-field>
<table mat-table [dataSource]="termsDatasource" matSort class="mat-elevation-z8">
<ng-container matColumnDef="code">
<th mat-header-cell *matHeaderCellDef style="width: 15%;" mat-sort-header sortActionDescription="Sort by Code"> Code </th>
<td mat-cell *matCellDef="let element"><b>{{element.code}}</b></td>
</ng-container>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef style="width: 15%;" mat-sort-header sortActionDescription="Sort by Name"> Name </th>
<td mat-cell *matCellDef="let element"> {{element.name}} </td>
</ng-container>
<ng-container matColumnDef="encoding">
<th mat-header-cell *matHeaderCellDef style="width: 10%;" mat-sort-header sortActionDescription="Sort by Encoding"> Encoding </th>
<td mat-cell *matCellDef="let element">{{element.encoding}}</td>
</ng-container>
<ng-container matColumnDef="synonyms">
<th mat-header-cell *matHeaderCellDef style="width: 40%;" mat-sort-header sortActionDescription="Sort by Synonyms"> Description </th>
<td mat-cell *matCellDef="let element" style="font-size: 0,7em;">
<span *ngFor="let s of element.synonyms" class="badge-label badge-info">
{{s.term}}
</span>
</td>
</ng-container>
<ng-container matColumnDef="buttons">
<th mat-header-cell *matHeaderCellDef style="text-align: right;"></th>
<td mat-cell *matCellDef="let element" class="table-buttons">
<button mat-stroked-button color="primary" (click)="editVocabularyTermDialog(element)">edit</button>
<button mat-stroked-button color="warn" (click)="deleteVocabularyTerm(element)">delete</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="colums"></tr>
<tr mat-row *matRowDef="let row; columns: colums;"></tr>
<!-- Row shown when there is no matching data. -->
<tr class="mat-row" *matNoDataRow>
<td class="mat-cell" colspan="5" style="padding: 0 16px;">No data matching the filter "{{input.value}}"</td>
</tr>
</table>