diff --git a/client/src/app/admin/instance/dataset/components/dataset/dataset-card.component.html b/client/src/app/admin/instance/dataset/components/dataset/dataset-card.component.html index 7ea2e12a0fbf3b11861000265f4b3392f9edcb52..0c757a815f1f2e48b01e0400d44d06f969d1fa5b 100644 --- a/client/src/app/admin/instance/dataset/components/dataset/dataset-card.component.html +++ b/client/src/app/admin/instance/dataset/components/dataset/dataset-card.component.html @@ -17,6 +17,10 @@ <span class="fas fa-cog"></span> </a> + <a routerLink="/admin/instance/configure-instance/{{ instance.name }}/dataset/image-list/{{dataset.name}}" class="btn btn-outline-primary" title="Configure images"> + <span class="fas fa-image"></span> + </a> + <a title="Edit this dataset" routerLink="/admin/instance/configure-instance/{{ instance.name }}/dataset/edit-dataset/{{dataset.name}}" class="btn btn-outline-primary"> <span class="fas fa-edit"></span> </a> diff --git a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html index 0200ca1dfdda93a57b2f69b22318700f2857c8db..94c24e99ae59622e94f8718a8062101b429250de 100644 --- a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html +++ b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html @@ -102,24 +102,6 @@ <input class="custom-control-input" type="checkbox" id="cone_search_plot_enabled" name="cone_search_plot_enabled" formControlName="cone_search_plot_enabled"> <label class="custom-control-label" for="cone_search_plot_enabled">Plot enabled</label> </div> - <app-image-table - [instanceDataPath]="instance.data_path" - [datasetDataPath]="form.controls.data_path.value" - [files]="files" - [filesIsLoading]="filesIsLoading" - [filesIsLoaded]="filesIsLoaded" - [imageList]="imageList" - [imageListIsLoading]="imageListIsLoading" - [imageListIsLoaded]="imageListIsLoaded" - [fitsImageLimits]="fitsImageLimits" - [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading" - [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded" - (loadRootDirectory)="loadRootDirectory.emit($event)" - (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)" - (addNewImage)="addNewImage.emit($event)" - (editImage)="editImage.emit($event)" - (deleteImage)="deleteImage.emit($event)"> - </app-image-table> </accordion-group> <accordion-group heading="Download" [isOpen]="true"> <div class="custom-control custom-switch"> diff --git a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.ts b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.ts index fd5949c5c132b78584d417ce8cf6ec0a50d76063..ea13faa10805d6f7d4d78ce48c6bf43bf93f30d2 100644 --- a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.ts +++ b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.ts @@ -30,18 +30,8 @@ export class DatasetFormComponent implements OnInit, OnChanges { @Input() filesIsLoading: boolean; @Input() filesIsLoaded: boolean; @Input() attributeList: Attribute[]; - @Input() imageList: Image[]; - @Input() imageListIsLoading: boolean; - @Input() imageListIsLoaded: boolean; - @Input() fitsImageLimits: FitsImageLimits; - @Input() fitsImageLimitsIsLoading: boolean; - @Input() fitsImageLimitsIsLoaded: boolean; @Output() changeSurvey: EventEmitter<number> = new EventEmitter(); @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter(); - @Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter(); - @Output() addNewImage: EventEmitter<Image> = new EventEmitter(); - @Output() editImage: EventEmitter<Image> = new EventEmitter(); - @Output() deleteImage: EventEmitter<Image> = new EventEmitter(); @Output() onSubmit: EventEmitter<Dataset> = new EventEmitter(); public isNewDataset = true; diff --git a/client/src/app/admin/instance/dataset/components/image/image-form.component.ts b/client/src/app/admin/instance/dataset/components/image/image-form.component.ts index 955277af3e0b2f387708bcdbb5c95e9e16b3876b..1ad3258bf23c08a84d3c0f4ea1102478d6915fb1 100644 --- a/client/src/app/admin/instance/dataset/components/image/image-form.component.ts +++ b/client/src/app/admin/instance/dataset/components/image/image-form.component.ts @@ -10,7 +10,7 @@ import { Component, Input, Output, OnInit, OnChanges, EventEmitter, SimpleChanges } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; -import { Image } from 'src/app/metamodel/models'; +import { Image, Dataset, Instance } from 'src/app/metamodel/models'; import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; @Component({ @@ -19,7 +19,8 @@ import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; }) export class ImageFormComponent implements OnInit, OnChanges { @Input() image: Image; - @Input() datasetDataPath: string; + @Input() instance: Instance; + @Input() dataset: Dataset; @Input() files: FileInfo[]; @Input() filesIsLoading: boolean; @Input() filesIsLoaded: boolean; @@ -55,7 +56,7 @@ export class ImageFormComponent implements OnInit, OnChanges { } onChangeFileSelect(path: string) { - this.loadRootDirectory.emit(`${this.datasetDataPath}${path}`); + this.loadRootDirectory.emit(`${this.instance.data_path}${this.dataset.data_path}${path}`); } onFileSelect(fileInfo: FileInfo) { diff --git a/client/src/app/admin/instance/dataset/components/image/image-table.component.html b/client/src/app/admin/instance/dataset/components/image/image-table.component.html index 3440ea61c9e2de12b911fd3a12d42ebdb981c7fd..a5256f236e3c0f916ebcf2e13ca3dfaafb1dc40e 100644 --- a/client/src/app/admin/instance/dataset/components/image/image-table.component.html +++ b/client/src/app/admin/instance/dataset/components/image/image-table.component.html @@ -1,6 +1,3 @@ -<button (click)="openModal(templateNewImage)" class="btn btn-outline-primary mt-3" type="button"> - <i class="far fa-image"></i> Add new image -</button> <div class="mt-2 table-responsive"> <table class="table"> <thead> @@ -26,7 +23,9 @@ <td class="align-middle">{{ image.pmin }}</td> <td class="align-middle">{{ image.pmax }}</td> <td class="align-middle"> - <a title="Edit this image" (click)="openModal(templateEditImage, image)" class="btn btn-outline-primary"> + <a title="Edit this image" + routerLink="/admin/instance/configure-instance/{{ instance.name }}/dataset/image-list/{{ dataset.name }}/edit-image/{{ image.id }}" + class="btn btn-outline-primary"> <span class="fas fa-edit"></span> </a> </td> @@ -41,56 +40,3 @@ </tbody> </table> </div> - -<ng-template #templateNewImage> - <div class="modal-header"> - <h4 class="modal-title pull-left"><strong>Add a new image</strong></h4> - </div> - <div class="modal-body"> - <app-image-form - [datasetDataPath]="datasetDataPath" - [files]="files" - [filesIsLoading]="filesIsLoading" - [filesIsLoaded]="filesIsLoaded" - [fitsImageLimits]="fitsImageLimits" - [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading" - [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded" - (loadRootDirectory)="onChangeDataPath($event)" - (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)" - (onSubmit)="addNewImage.emit($event)" - #formAddDatasetFamily> - <button [disabled]="!formAddDatasetFamily.form.valid || formAddDatasetFamily.form.pristine" (click)="modalRef.hide()" type="submit" class="btn btn-primary"> - <span class="fa fa-database"></span> Add new image - </button> - - <button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button> - </app-image-form> - </div> -</ng-template> - -<ng-template #templateEditImage> - <div class="modal-header"> - <h4 class="modal-title pull-left"><strong>Edit image</strong></h4> - </div> - <div class="modal-body"> - <app-image-form - [image]="imageForEdit" - [datasetDataPath]="datasetDataPath" - [files]="files" - [filesIsLoading]="filesIsLoading" - [filesIsLoaded]="filesIsLoaded" - [fitsImageLimits]="fitsImageLimits" - [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading" - [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded" - (loadRootDirectory)="onChangeDataPath($event)" - (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)" - (onSubmit)="editImage.emit($event)" - #formAddDatasetFamily> - <button [disabled]="!formAddDatasetFamily.form.valid || formAddDatasetFamily.form.pristine" (click)="modalRef.hide()" type="submit" class="btn btn-primary"> - <span class="fa fa-database"></span> Edit image - </button> - - <button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button> - </app-image-form> - </div> -</ng-template> \ No newline at end of file diff --git a/client/src/app/admin/instance/dataset/components/image/image-table.component.ts b/client/src/app/admin/instance/dataset/components/image/image-table.component.ts index fd75a9d9329ea3923ff93e9812a44118148e5317..669b430624abd04f63f8b625fdc2a8a38c188421 100644 --- a/client/src/app/admin/instance/dataset/components/image/image-table.component.ts +++ b/client/src/app/admin/instance/dataset/components/image/image-table.component.ts @@ -7,13 +7,9 @@ * file that was distributed with this source code. */ -import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter, TemplateRef } from '@angular/core'; +import { Component, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core'; -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; - -import { Image } from 'src/app/metamodel/models'; -import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; +import { Dataset, Image, Instance } from 'src/app/metamodel/models'; @Component({ selector: 'app-image-table', @@ -21,34 +17,8 @@ import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; changeDetection: ChangeDetectionStrategy.OnPush }) export class ImageTableComponent { - @Input() instanceDataPath: string; - @Input() datasetDataPath: string; - @Input() files: FileInfo[]; - @Input() filesIsLoading: boolean; - @Input() filesIsLoaded: boolean; + @Input() instance: Instance; + @Input() dataset: Dataset; @Input() imageList: Image[]; - @Input() imageListIsLoading: boolean; - @Input() imageListIsLoaded: boolean; - @Input() fitsImageLimits: FitsImageLimits; - @Input() fitsImageLimitsIsLoading: boolean; - @Input() fitsImageLimitsIsLoaded: boolean; - @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter(); - @Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter(); - @Output() addNewImage: EventEmitter<Image> = new EventEmitter(); - @Output() editImage: EventEmitter<Image> = new EventEmitter(); @Output() deleteImage: EventEmitter<Image> = new EventEmitter(); - - modalRef: BsModalRef; - imageForEdit: Image; - - constructor(private modalService: BsModalService) { } - - openModal(template: TemplateRef<any>, image: Image = null) { - this.imageForEdit = image; - this.modalRef = this.modalService.show(template); - } - - onChangeDataPath(path: string) { - this.loadRootDirectory.emit(`${this.instanceDataPath}${path}`); - } } diff --git a/client/src/app/admin/instance/dataset/containers/edit-dataset.component.html b/client/src/app/admin/instance/dataset/containers/edit-dataset.component.html index abdb60b522e15b6276fe4c08122c8fb23bb5a8fe..a1ab6612fe19ea7f16c3bd5ad891b6028f25e5a3 100644 --- a/client/src/app/admin/instance/dataset/containers/edit-dataset.component.html +++ b/client/src/app/admin/instance/dataset/containers/edit-dataset.component.html @@ -37,18 +37,8 @@ [filesIsLoading]="filesIsLoading | async" [filesIsLoaded]="filesIsLoaded | async" [attributeList]="attributeList | async" - [imageList]="imageList | async" - [imageListIsLoading]="imageListIsLoading | async" - [imageListIsLoaded]="imageListIsLoaded | async" - [fitsImageLimits]="fitsImageLimits | async" - [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading | async" - [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded | async" (changeSurvey)="loadTableList($event)" (loadRootDirectory)="loadRootDirectory($event)" - (retrieveFitsImageLimits)="retrieveFitsImageLimits($event)" - (addNewImage)="addNewImage($event)" - (editImage)="editImage($event)" - (deleteImage)="deleteImage($event)" (onSubmit)="editDataset($event)" #formDataset> <button [disabled]="!formDataset.form.valid || formDataset.form.pristine" type="submit" class="btn btn-primary"> diff --git a/client/src/app/admin/instance/dataset/containers/edit-dataset.component.ts b/client/src/app/admin/instance/dataset/containers/edit-dataset.component.ts index a323f77f2f5539b9522925ca6cbc7a8d56ad8dd1..ef598be41b686deebc8dccb1ce8f13af615019fe 100644 --- a/client/src/app/admin/instance/dataset/containers/edit-dataset.component.ts +++ b/client/src/app/admin/instance/dataset/containers/edit-dataset.component.ts @@ -8,13 +8,12 @@ */ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { Store } from '@ngrx/store'; import { Instance, Survey, DatasetFamily, Dataset, Attribute, Image } from 'src/app/metamodel/models'; -import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; +import { FileInfo } from 'src/app/admin/store/models'; import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; import * as datasetActions from 'src/app/metamodel/actions/dataset.actions'; import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector'; @@ -27,9 +26,6 @@ import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector import * as adminFileExplorerActions from 'src/app/admin/store/actions/admin-file-explorer.actions'; import * as adminFileExplorerSelector from 'src/app/admin/store/selectors/admin-file-explorer.selector'; import * as imageActions from 'src/app/metamodel/actions/image.actions'; -import * as imageSelector from 'src/app/metamodel/selectors/image.selector'; -import * as fitsImageActions from 'src/app/admin/store/actions/fits-image.actions'; -import * as fitsImageSelector from 'src/app/admin/store/selectors/fits-image.selector'; @Component({ selector: 'app-edit-dataset', @@ -56,14 +52,8 @@ export class EditDatasetComponent implements OnInit { public files: Observable<FileInfo[]>; public filesIsLoading: Observable<boolean>; public filesIsLoaded: Observable<boolean>; - public imageList: Observable<Image[]>; - public imageListIsLoading: Observable<boolean>; - public imageListIsLoaded: Observable<boolean>; - public fitsImageLimits: Observable<FitsImageLimits>; - public fitsImageLimitsIsLoading: Observable<boolean>; - public fitsImageLimitsIsLoaded: Observable<boolean>; - constructor(private store: Store<{ }>, private route: ActivatedRoute) { + constructor(private store: Store<{ }>) { this.instance = store.select(instanceSelector.selectInstanceByRouteName); this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute); this.datasetList = store.select(datasetSelector.selectAllDatasets); @@ -84,12 +74,6 @@ export class EditDatasetComponent implements OnInit { this.files = store.select(adminFileExplorerSelector.selectFiles); this.filesIsLoading = store.select(adminFileExplorerSelector.selectFilesIsLoading); this.filesIsLoaded = store.select(adminFileExplorerSelector.selectFilesIsLoaded); - this.imageList = store.select(imageSelector.selectAllImages); - this.imageListIsLoading = store.select(imageSelector.selectImageListIsLoading); - this.imageListIsLoaded = store.select(imageSelector.selectImageListIsLoaded); - this.fitsImageLimits = store.select(fitsImageSelector.selectFitsImageLimits); - this.fitsImageLimitsIsLoading = store.select(fitsImageSelector.selectFitsImageLimitsIsLoading); - this.fitsImageLimitsIsLoaded = store.select(fitsImageSelector.selectFitsImageLimitsIsLoaded); } ngOnInit() { @@ -105,22 +89,6 @@ export class EditDatasetComponent implements OnInit { this.store.dispatch(adminFileExplorerActions.loadFiles({ path })); } - addNewImage(image: Image) { - this.store.dispatch(imageActions.addImage({ image })); - } - - editImage(image: Image) { - this.store.dispatch(imageActions.editImage({ image })); - } - - deleteImage(image: Image) { - this.store.dispatch(imageActions.deleteImage({ image })); - } - - retrieveFitsImageLimits(filePath: string) { - this.store.dispatch(fitsImageActions.retrieveFitsImageLimits({ filePath })); - } - editDataset(dataset: Dataset) { this.store.dispatch(datasetActions.editDataset({ dataset })); } diff --git a/client/src/app/admin/instance/dataset/containers/edit-image.component.html b/client/src/app/admin/instance/dataset/containers/edit-image.component.html new file mode 100644 index 0000000000000000000000000000000000000000..177f0ddc2b5d235f6939d11d3aa0ab39be2032b2 --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/edit-image.component.html @@ -0,0 +1,47 @@ +<app-spinner *ngIf="(datasetListIsLoading | async) || (imageListIsLoading | async)"></app-spinner> + +<div *ngIf="(datasetListIsLoaded | async) && (imageListIsLoaded | async)" class="container-fluid"> + <nav aria-label="breadcrumb"> + <ol class="breadcrumb"> + <li class="breadcrumb-item"><a routerLink="/admin/instance/instance-list">Instances</a></li> + <li class="breadcrumb-item"> + <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/dataset-list"> + Configure instance {{ (instance | async).label }} + </a> + </li> + <li class="breadcrumb-item"> + <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}"> + {{ (dataset | async).label }} image list + </a> + </li> + <li class="breadcrumb-item active" aria-current="page">Edit image</li> + </ol> + </nav> +</div> + +<div *ngIf="(datasetListIsLoaded | async) && (imageListIsLoaded | async)" class="container"> + <div class="row"> + <div class="col-12"> + <app-image-form + [image]="image | async" + [instance]="instance | async" + [dataset]="dataset | async" + [files]="files | async" + [filesIsLoading]="filesIsLoading | async" + [filesIsLoaded]="filesIsLoaded | async" + [fitsImageLimits]="fitsImageLimits | async" + [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading | async" + [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded | async" + (loadRootDirectory)="loadRootDirectory($event)" + (retrieveFitsImageLimits)="retrieveFitsImageLimits($event)" + (onSubmit)="editImage($event)" + #formEditImage> + <button [disabled]="!formEditImage.form.valid || formEditImage.form.pristine" type="submit" class="btn btn-primary"> + <span class="fa fa-database"></span> Edit image + </button> + + <button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}" type="button" class="btn btn-danger">Cancel</button> + </app-image-form> + </div> + </div> +</div> diff --git a/client/src/app/admin/instance/dataset/containers/edit-image.component.ts b/client/src/app/admin/instance/dataset/containers/edit-image.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..b9b2f033291812dea41a2d13869930d2b765f647 --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/edit-image.component.ts @@ -0,0 +1,76 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Component } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { Store } from '@ngrx/store'; + +import { Instance, Dataset, Image } from 'src/app/metamodel/models'; +import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; +import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; +import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; +import * as adminFileExplorerActions from 'src/app/admin/store/actions/admin-file-explorer.actions'; +import * as adminFileExplorerSelector from 'src/app/admin/store/selectors/admin-file-explorer.selector'; +import * as imageActions from 'src/app/metamodel/actions/image.actions'; +import * as imageSelector from 'src/app/metamodel/selectors/image.selector'; +import * as fitsImageActions from 'src/app/admin/store/actions/fits-image.actions'; +import * as fitsImageSelector from 'src/app/admin/store/selectors/fits-image.selector'; + +@Component({ + selector: 'app-edit-image', + templateUrl: 'edit-image.component.html' +}) +export class EditImageComponent { + public instance: Observable<Instance>; + public dataset: Observable<Dataset>; + public datasetListIsLoading: Observable<boolean>; + public datasetListIsLoaded: Observable<boolean>; + public files: Observable<FileInfo[]>; + public filesIsLoading: Observable<boolean>; + public filesIsLoaded: Observable<boolean>; + public fitsImageLimits: Observable<FitsImageLimits>; + public fitsImageLimitsIsLoading: Observable<boolean>; + public fitsImageLimitsIsLoaded: Observable<boolean>; + public image: Observable<Image>; + public imageListIsLoading: Observable<boolean>; + public imageListIsLoaded: Observable<boolean>; + + constructor(private store: Store<{ }>) { + this.instance = store.select(instanceSelector.selectInstanceByRouteName); + this.dataset = store.select(datasetSelector.selectDatasetByRouteName); + this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); + this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); + this.files = store.select(adminFileExplorerSelector.selectFiles); + this.filesIsLoading = store.select(adminFileExplorerSelector.selectFilesIsLoading); + this.filesIsLoaded = store.select(adminFileExplorerSelector.selectFilesIsLoaded); + this.fitsImageLimits = store.select(fitsImageSelector.selectFitsImageLimits); + this.fitsImageLimitsIsLoading = store.select(fitsImageSelector.selectFitsImageLimitsIsLoading); + this.fitsImageLimitsIsLoaded = store.select(fitsImageSelector.selectFitsImageLimitsIsLoaded); + this.image = store.select(imageSelector.selectImageByRouteId); + this.imageListIsLoading = store.select(imageSelector.selectImageListIsLoading); + this.imageListIsLoaded = store.select(imageSelector.selectImageListIsLoaded); + } + + ngOnInit() { + this.store.dispatch(imageActions.loadImageList()); + } + + loadRootDirectory(path: string) { + this.store.dispatch(adminFileExplorerActions.loadFiles({ path })); + } + + retrieveFitsImageLimits(filePath: string) { + this.store.dispatch(fitsImageActions.retrieveFitsImageLimits({ filePath })); + } + + editImage(image: Image) { + this.store.dispatch(imageActions.editImage({ image })); + } +} \ No newline at end of file diff --git a/client/src/app/admin/instance/dataset/containers/image-list.component.html b/client/src/app/admin/instance/dataset/containers/image-list.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8826de3f86320fb6a2e5b27587fe78b49fda4026 --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/image-list.component.html @@ -0,0 +1,38 @@ +<div class="container-fluid"> + <nav aria-label="breadcrumb"> + <ol class="breadcrumb"> + <li class="breadcrumb-item"><a routerLink="/admin/instance/instance-list">Instances</a></li> + <li class="breadcrumb-item"> + <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/dataset-list"> + Configure instance {{ (instance | async).label }} + </a> + </li> + <li class="breadcrumb-item active" aria-current="page">{{ (dataset | async).label }} image list</li> + </ol> + </nav> + + <app-spinner *ngIf="imageListIsLoading | async"></app-spinner> + + <ng-container *ngIf="imageListIsLoaded | async"> + <div class="row"> + <div class="col-12"> + <button title="Add a new image" + class="btn btn-outline-success float-right" + routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}/new-image"> + <span class="fas fa-plus"></span> New image + </button> + </div> + </div> + + <div class="row mt-1"> + <div class="col-12"> + <app-image-table + [instance]="instance | async" + [dataset]="dataset | async" + [imageList]="imageList | async" + (deleteImage)="deleteImage($event)"> + </app-image-table> + </div> + </div> + </ng-container> +</div> diff --git a/client/src/app/admin/instance/dataset/containers/image-list.component.ts b/client/src/app/admin/instance/dataset/containers/image-list.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3b12da9c6ad217ad21ead1cf453a854684e101a --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/image-list.component.ts @@ -0,0 +1,51 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Component, OnInit } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { Store } from '@ngrx/store'; + +import { Image, Instance, Dataset } from 'src/app/metamodel/models'; +import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; +import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; +import * as imageActions from 'src/app/metamodel/actions/image.actions'; +import * as imageSelector from 'src/app/metamodel/selectors/image.selector'; + +@Component({ + selector: 'app-image-list', + templateUrl: 'image-list.component.html' +}) +export class ImageListComponent implements OnInit { + public instance: Observable<Instance>; + public dataset: Observable<Dataset>; + public datasetListIsLoading: Observable<boolean>; + public datasetListIsLoaded: Observable<boolean>; + public imageList: Observable<Image[]>; + public imageListIsLoading: Observable<boolean>; + public imageListIsLoaded: Observable<boolean>; + + constructor(private store: Store<{ }>) { + this.instance = store.select(instanceSelector.selectInstanceByRouteName); + this.dataset = store.select(datasetSelector.selectDatasetByRouteName); + this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); + this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); + this.imageList = store.select(imageSelector.selectAllImages); + this.imageListIsLoading = store.select(imageSelector.selectImageListIsLoading); + this.imageListIsLoaded = store.select(imageSelector.selectImageListIsLoaded); + } + + ngOnInit() { + this.store.dispatch(imageActions.loadImageList()); + } + + deleteImage(image: Image) { + this.store.dispatch(imageActions.deleteImage({ image })); + } +} \ No newline at end of file diff --git a/client/src/app/admin/instance/dataset/containers/new-image.component.html b/client/src/app/admin/instance/dataset/containers/new-image.component.html new file mode 100644 index 0000000000000000000000000000000000000000..8560f4ab7d54d2329a58d1ba85c0cbe56d93af72 --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/new-image.component.html @@ -0,0 +1,46 @@ +<app-spinner *ngIf="datasetListIsLoading | async"></app-spinner> + +<div *ngIf="datasetListIsLoaded | async" class="container-fluid"> + <nav aria-label="breadcrumb"> + <ol class="breadcrumb"> + <li class="breadcrumb-item"><a routerLink="/admin/instance/instance-list">Instances</a></li> + <li class="breadcrumb-item"> + <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/dataset-list"> + Configure instance {{ (instance | async).label }} + </a> + </li> + <li class="breadcrumb-item"> + <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}"> + {{ (dataset | async).label }} image list + </a> + </li> + <li class="breadcrumb-item active" aria-current="page">Add a new image</li> + </ol> + </nav> +</div> + +<div *ngIf="datasetListIsLoaded | async" class="container"> + <div class="row"> + <div class="col-12"> + <app-image-form + [instance]="instance | async" + [dataset]="dataset | async" + [files]="files | async" + [filesIsLoading]="filesIsLoading | async" + [filesIsLoaded]="filesIsLoaded | async" + [fitsImageLimits]="fitsImageLimits | async" + [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading | async" + [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded | async" + (loadRootDirectory)="loadRootDirectory($event)" + (retrieveFitsImageLimits)="retrieveFitsImageLimits($event)" + (onSubmit)="addNewImage($event)" + #formAddImage> + <button [disabled]="!formAddImage.form.valid || formAddImage.form.pristine" type="submit" class="btn btn-primary"> + <span class="fa fa-database"></span> Add new image + </button> + + <button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/image-list/{{ (dataset | async).name }}" type="button" class="btn btn-danger">Cancel</button> + </app-image-form> + </div> + </div> +</div> diff --git a/client/src/app/admin/instance/dataset/containers/new-image.component.ts b/client/src/app/admin/instance/dataset/containers/new-image.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..5dd243e66f6b60a92f555d549d29f87efd55fa0a --- /dev/null +++ b/client/src/app/admin/instance/dataset/containers/new-image.component.ts @@ -0,0 +1,65 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +import { Component } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { Store } from '@ngrx/store'; + +import { Instance, Dataset, Image } from 'src/app/metamodel/models'; +import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models'; +import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector'; +import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector'; +import * as adminFileExplorerActions from 'src/app/admin/store/actions/admin-file-explorer.actions'; +import * as adminFileExplorerSelector from 'src/app/admin/store/selectors/admin-file-explorer.selector'; +import * as imageActions from 'src/app/metamodel/actions/image.actions'; +import * as fitsImageActions from 'src/app/admin/store/actions/fits-image.actions'; +import * as fitsImageSelector from 'src/app/admin/store/selectors/fits-image.selector'; + +@Component({ + selector: 'app-new-image', + templateUrl: 'new-image.component.html' +}) +export class NewImageComponent { + public instance: Observable<Instance>; + public dataset: Observable<Dataset>; + public datasetListIsLoading: Observable<boolean>; + public datasetListIsLoaded: Observable<boolean>; + public files: Observable<FileInfo[]>; + public filesIsLoading: Observable<boolean>; + public filesIsLoaded: Observable<boolean>; + public fitsImageLimits: Observable<FitsImageLimits>; + public fitsImageLimitsIsLoading: Observable<boolean>; + public fitsImageLimitsIsLoaded: Observable<boolean>; + + constructor(private store: Store<{ }>) { + this.instance = store.select(instanceSelector.selectInstanceByRouteName); + this.dataset = store.select(datasetSelector.selectDatasetByRouteName); + this.datasetListIsLoading = store.select(datasetSelector.selectDatasetListIsLoading); + this.datasetListIsLoaded = store.select(datasetSelector.selectDatasetListIsLoaded); + this.files = store.select(adminFileExplorerSelector.selectFiles); + this.filesIsLoading = store.select(adminFileExplorerSelector.selectFilesIsLoading); + this.filesIsLoaded = store.select(adminFileExplorerSelector.selectFilesIsLoaded); + this.fitsImageLimits = store.select(fitsImageSelector.selectFitsImageLimits); + this.fitsImageLimitsIsLoading = store.select(fitsImageSelector.selectFitsImageLimitsIsLoading); + this.fitsImageLimitsIsLoaded = store.select(fitsImageSelector.selectFitsImageLimitsIsLoaded); + } + + loadRootDirectory(path: string) { + this.store.dispatch(adminFileExplorerActions.loadFiles({ path })); + } + + retrieveFitsImageLimits(filePath: string) { + this.store.dispatch(fitsImageActions.retrieveFitsImageLimits({ filePath })); + } + + addNewImage(image: Image) { + this.store.dispatch(imageActions.addImage({ image })); + } +} \ No newline at end of file diff --git a/client/src/app/admin/instance/dataset/dataset-routing.module.ts b/client/src/app/admin/instance/dataset/dataset-routing.module.ts index 5779ddc77cd552c295f7506e3da3d7ebda1e10ac..31d8edf444892a652bd806ae79a6106bd88094cb 100644 --- a/client/src/app/admin/instance/dataset/dataset-routing.module.ts +++ b/client/src/app/admin/instance/dataset/dataset-routing.module.ts @@ -14,12 +14,18 @@ import { DatasetListComponent } from './containers/dataset-list.component'; import { NewDatasetComponent } from './containers/new-dataset.component'; import { EditDatasetComponent } from './containers/edit-dataset.component'; import { AttributeListComponent } from './containers/attribute-list.component'; +import { ImageListComponent } from './containers/image-list.component'; +import { NewImageComponent } from './containers/new-image.component'; +import { EditImageComponent } from './containers/edit-image.component'; const routes: Routes = [ { path: 'dataset-list', component: DatasetListComponent }, { path: 'new-dataset', component: NewDatasetComponent }, { path: 'edit-dataset/:dname', component: EditDatasetComponent }, { path: 'configure-dataset/:dname', component: AttributeListComponent }, + { path: 'image-list/:dname', component: ImageListComponent }, + { path: 'image-list/:dname/new-image', component: NewImageComponent }, + { path: 'image-list/:dname/edit-image/:id', component: EditImageComponent } ]; /** @@ -36,5 +42,8 @@ export const routedComponents = [ DatasetListComponent, NewDatasetComponent, EditDatasetComponent, - AttributeListComponent + AttributeListComponent, + ImageListComponent, + NewImageComponent, + EditImageComponent ]; diff --git a/client/src/app/metamodel/effects/image.effects.ts b/client/src/app/metamodel/effects/image.effects.ts index 59dce9ad4f4ee09e03a35b7dea5e40996f7f6b6c..dcfc1d5cd56fd61fc98504dd50c321658ea4400c 100644 --- a/client/src/app/metamodel/effects/image.effects.ts +++ b/client/src/app/metamodel/effects/image.effects.ts @@ -8,6 +8,7 @@ */ import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; import { Store } from '@ngrx/store'; @@ -18,6 +19,7 @@ import { ToastrService } from 'ngx-toastr'; import * as imageActions from '../actions/image.actions'; import { ImageService } from '../services/image.service'; import * as datasetSelector from '../selectors/dataset.selector'; +import * as instanceSelector from '../selectors/instance.selector'; /** * @class @@ -64,7 +66,14 @@ export class ImageEffects { addImageSuccess$ = createEffect(() => this.actions$.pipe( ofType(imageActions.addImageSuccess), - tap(() => this.toastr.success('Image successfully added', 'The new image was added into the database')) + concatLatestFrom(() => [ + this.store.select(instanceSelector.selectInstanceNameByRoute), + this.store.select(datasetSelector.selectDatasetNameByRoute) + ]), + tap(([, instanceName, datasetName]) => { + this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/dataset/image-list/${datasetName}`); + this.toastr.success('Image successfully added', 'The new image was added into the database'); + }) ), { dispatch: false } ); @@ -99,7 +108,14 @@ export class ImageEffects { editImageSuccess$ = createEffect(() => this.actions$.pipe( ofType(imageActions.editImageSuccess), - tap(() => this.toastr.success('Image successfully edited', 'The existing image has been edited into the database')) + concatLatestFrom(() => [ + this.store.select(instanceSelector.selectInstanceNameByRoute), + this.store.select(datasetSelector.selectDatasetNameByRoute) + ]), + tap(([, instanceName, datasetName]) => { + this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/dataset/image-list/${datasetName}`); + this.toastr.success('Image successfully edited', 'The existing image has been edited into the database'); + }) ), { dispatch: false } ); @@ -151,6 +167,7 @@ export class ImageEffects { constructor( private actions$: Actions, private imageService: ImageService, + private router: Router, private toastr: ToastrService, private store: Store<{ }> ) {} diff --git a/client/src/app/metamodel/selectors/image.selector.ts b/client/src/app/metamodel/selectors/image.selector.ts index 5902eb3fc5a09b464c4c56db3669e774db8838a8..ce5e4842d2bd031317d5f99d3bbd70abd5670b3d 100644 --- a/client/src/app/metamodel/selectors/image.selector.ts +++ b/client/src/app/metamodel/selectors/image.selector.ts @@ -46,3 +46,9 @@ export const selectImageListIsLoaded = createSelector( selectImageState, fromImage.selectImageListIsLoaded ); + +export const selectImageByRouteId = createSelector( + selectImageEntities, + reducer.selectRouterState, + (entities, router) => entities[router.state.params.id] +); \ No newline at end of file