From 42c58b205fe3e81ff9383b053f9297662ea83ec2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Mon, 11 Apr 2022 12:22:17 +0200
Subject: [PATCH] Image list + cone-search => done

---
 .../add-criteria-family.component.html        |  2 +-
 .../dataset/dataset-form.component.ts         |  3 -
 .../components/image/add-image.component.html | 34 +++++++++
 .../components/image/add-image.component.ts   | 43 +++++++++++
 .../image/edit-image.component.html           | 31 ++++++++
 .../components/image/edit-image.component.ts  | 44 +++++++++++
 .../image/image-form.component.html           |  4 +
 .../components/image/image-form.component.ts  |  1 +
 .../image/image-list.component.html           | 75 +++++++++++++++++++
 .../components/image/image-list.component.ts  | 26 +++++++
 .../image/image-table.component.html          | 42 -----------
 .../components/image/image-table.component.ts | 24 ------
 .../dataset/components/image/index.ts         |  8 +-
 .../add-output-category.component.html        |  2 +-
 .../add-output-family.component.html          |  2 +-
 .../containers/attribute-list.component.html  | 31 ++++++--
 .../containers/attribute-list.component.ts    | 49 +++++++++++-
 .../containers/edit-image.component.html      | 52 -------------
 .../containers/edit-image.component.ts        | 72 ------------------
 .../containers/image-list.component.html      | 43 -----------
 .../containers/image-list.component.ts        | 47 ------------
 .../containers/new-image.component.html       | 51 -------------
 .../dataset/containers/new-image.component.ts | 65 ----------------
 .../dataset/dataset-routing.module.ts         |  9 ---
 .../app/core/containers/app.component.html    |  5 +-
 .../result/image-list-result.component.html   |  3 +-
 .../app/metamodel/effects/image.effects.ts    | 22 +-----
 .../src/app/metamodel/models/image.model.ts   |  1 +
 .../doctrine-proxy/__CG__AppEntityImage.php   | 26 ++++++-
 server/src/Action/ImageAction.php             |  2 +
 server/src/Action/ImageListAction.php         |  2 +
 server/src/Entity/Image.php                   | 18 +++++
 32 files changed, 393 insertions(+), 446 deletions(-)
 create mode 100644 client/src/app/admin/instance/dataset/components/image/add-image.component.html
 create mode 100644 client/src/app/admin/instance/dataset/components/image/add-image.component.ts
 create mode 100644 client/src/app/admin/instance/dataset/components/image/edit-image.component.html
 create mode 100644 client/src/app/admin/instance/dataset/components/image/edit-image.component.ts
 create mode 100644 client/src/app/admin/instance/dataset/components/image/image-list.component.html
 create mode 100644 client/src/app/admin/instance/dataset/components/image/image-list.component.ts
 delete mode 100644 client/src/app/admin/instance/dataset/components/image/image-table.component.html
 delete mode 100644 client/src/app/admin/instance/dataset/components/image/image-table.component.ts
 delete mode 100644 client/src/app/admin/instance/dataset/containers/edit-image.component.html
 delete mode 100644 client/src/app/admin/instance/dataset/containers/edit-image.component.ts
 delete mode 100644 client/src/app/admin/instance/dataset/containers/image-list.component.html
 delete mode 100644 client/src/app/admin/instance/dataset/containers/image-list.component.ts
 delete mode 100644 client/src/app/admin/instance/dataset/containers/new-image.component.html
 delete mode 100644 client/src/app/admin/instance/dataset/containers/new-image.component.ts

diff --git a/client/src/app/admin/instance/dataset/components/criteria-family/add-criteria-family.component.html b/client/src/app/admin/instance/dataset/components/criteria-family/add-criteria-family.component.html
index e5acc955..7de9cffb 100644
--- a/client/src/app/admin/instance/dataset/components/criteria-family/add-criteria-family.component.html
+++ b/client/src/app/admin/instance/dataset/components/criteria-family/add-criteria-family.component.html
@@ -1,6 +1,6 @@
 <div class="row mb-1">
     <div class="col-12">
-        <button title="Add new criteria family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
+        <button title="Add new criteria family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
             <span class="fas fa-plus"></span> New criteria family
         </button>
     </div>
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 378277f6..9819ca21 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
@@ -127,7 +127,6 @@ export class DatasetFormComponent implements OnInit, OnChanges {
             this.form.controls.download_csv.enable();
             this.form.controls.download_ascii.enable();
             this.form.controls.download_vo.enable();
-            this.form.controls.download_archive.enable();
         } else {
             this.form.controls.download_opened.setValue(false);
             this.form.controls.download_opened.disable();
@@ -139,8 +138,6 @@ export class DatasetFormComponent implements OnInit, OnChanges {
             this.form.controls.download_ascii.disable();
             this.form.controls.download_vo.setValue(false);
             this.form.controls.download_vo.disable();
-            this.form.controls.download_archive.setValue(false);
-            this.form.controls.download_archive.disable();
         }
     }
 
diff --git a/client/src/app/admin/instance/dataset/components/image/add-image.component.html b/client/src/app/admin/instance/dataset/components/image/add-image.component.html
new file mode 100644
index 00000000..57b85cb7
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/add-image.component.html
@@ -0,0 +1,34 @@
+<div class="row mb-1">
+    <div class="col-12">
+        <button title="Add new image" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
+            <span class="fas fa-plus"></span> New image
+        </button>
+    </div>
+</div>
+
+<ng-template #template>
+    <div class="modal-header">
+        <h4 class="modal-title pull-left"><strong>Add new image</strong></h4>
+    </div>
+    <div class="modal-body">
+        <app-image-form 
+            [instance]="instance"
+            [dataset]="dataset"
+            [files]="files"
+            [filesIsLoading]="filesIsLoading"
+            [filesIsLoaded]="filesIsLoaded"
+            [fitsImageLimits]="fitsImageLimits"
+            [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
+            [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
+            (loadRootDirectory)="loadRootDirectory.emit($event)"
+            (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
+            (onSubmit)="add.emit($event); modalRef.hide()"
+            #formNewImage>
+            <button [disabled]="!formNewImage.form.valid || formNewImage.form.pristine" type="submit" class="btn btn-primary">
+                <span class="fa fa-database"></span> Add image
+            </button>
+            &nbsp;
+            <button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button>
+        </app-image-form>
+    </div>
+</ng-template>
diff --git a/client/src/app/admin/instance/dataset/components/image/add-image.component.ts b/client/src/app/admin/instance/dataset/components/image/add-image.component.ts
new file mode 100644
index 00000000..e5c2697f
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/add-image.component.ts
@@ -0,0 +1,43 @@
+/**
+ * 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, ChangeDetectionStrategy, TemplateRef, Input, Output, EventEmitter } from '@angular/core';
+
+import { BsModalService } from 'ngx-bootstrap/modal';
+import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
+
+import { Dataset, Image, Instance } from 'src/app/metamodel/models';
+import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
+
+@Component({
+    selector: 'app-add-image',
+    templateUrl: 'add-image.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class AddImageComponent {
+    @Input() instance: Instance;
+    @Input() dataset: Dataset;
+    @Input() files: FileInfo[];
+    @Input() filesIsLoading: boolean;
+    @Input() filesIsLoaded: boolean;
+    @Input() fitsImageLimits: FitsImageLimits;
+    @Input() fitsImageLimitsIsLoading: boolean;
+    @Input() fitsImageLimitsIsLoaded: boolean;
+    @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
+    @Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
+    @Output() add: EventEmitter<Image> = new EventEmitter();
+
+    modalRef: BsModalRef;
+
+    constructor(private modalService: BsModalService) { }
+
+    openModal(template: TemplateRef<any>) {
+        this.modalRef = this.modalService.show(template);
+    }
+}
diff --git a/client/src/app/admin/instance/dataset/components/image/edit-image.component.html b/client/src/app/admin/instance/dataset/components/image/edit-image.component.html
new file mode 100644
index 00000000..3e964860
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/edit-image.component.html
@@ -0,0 +1,31 @@
+<button title="Edit this image" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-primary">
+    <span class="fas fa-edit"></span>
+</button>
+
+<ng-template #template>
+    <div class="modal-header">
+        <h4 class="modal-title pull-left"><strong>Edit image</strong></h4>
+    </div>
+    <div class="modal-body">
+        <app-image-form 
+            [image]="image"
+            [instance]="instance"
+            [dataset]="dataset"
+            [files]="files"
+            [filesIsLoading]="filesIsLoading"
+            [filesIsLoaded]="filesIsLoaded"
+            [fitsImageLimits]="fitsImageLimits"
+            [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
+            [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
+            (loadRootDirectory)="loadRootDirectory.emit($event)"
+            (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
+            (onSubmit)="edit.emit($event); modalRef.hide()"
+            #formEditImage>
+            <button [disabled]="!formEditImage.form.valid || formEditImage.form.pristine" type="submit" class="btn btn-primary">
+                <span class="fa fa-database"></span> Update image information
+            </button>
+            &nbsp;
+            <button (click)="modalRef.hide()" type="button" class="btn btn-danger">Cancel</button>
+        </app-image-form>
+    </div>
+</ng-template>
diff --git a/client/src/app/admin/instance/dataset/components/image/edit-image.component.ts b/client/src/app/admin/instance/dataset/components/image/edit-image.component.ts
new file mode 100644
index 00000000..98934f25
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/edit-image.component.ts
@@ -0,0 +1,44 @@
+/**
+ * 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, Input, ChangeDetectionStrategy, TemplateRef, Output, EventEmitter } from '@angular/core';
+
+import { BsModalService } from 'ngx-bootstrap/modal';
+import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
+
+import { Dataset, Image, Instance } from 'src/app/metamodel/models';
+import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
+
+@Component({
+    selector: 'app-edit-image',
+    templateUrl: 'edit-image.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class EditImageComponent {
+    @Input() image: Image;
+    @Input() instance: Instance;
+    @Input() dataset: Dataset;
+    @Input() files: FileInfo[];
+    @Input() filesIsLoading: boolean;
+    @Input() filesIsLoaded: boolean;
+    @Input() fitsImageLimits: FitsImageLimits;
+    @Input() fitsImageLimitsIsLoading: boolean;
+    @Input() fitsImageLimitsIsLoaded: boolean;
+    @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
+    @Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
+    @Output() edit: EventEmitter<Image> = new EventEmitter();
+
+    modalRef: BsModalRef;
+
+    constructor(private modalService: BsModalService) { }
+
+    openModal(template: TemplateRef<any>) {
+        this.modalRef = this.modalService.show(template);
+    }
+}
diff --git a/client/src/app/admin/instance/dataset/components/image/image-form.component.html b/client/src/app/admin/instance/dataset/components/image/image-form.component.html
index 1c8392e9..13c2997e 100644
--- a/client/src/app/admin/instance/dataset/components/image/image-form.component.html
+++ b/client/src/app/admin/instance/dataset/components/image/image-form.component.html
@@ -12,6 +12,10 @@
         (loadDirectory)="onChangeFileSelect($event)"
         (select)="onFileSelect($event)">
     </app-path-select-form-control>
+    <div class="form-group">
+        <label for="label">Label</label>
+        <input type="text" class="form-control" id="label" name="label" formControlName="label">
+    </div>
     <div class="form-group">
         <label for="file_size">File size</label>
         <input type="number" class="form-control" id="file_size" name="file_size" formControlName="file_size">
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 1ad3258b..28b66757 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
@@ -32,6 +32,7 @@ export class ImageFormComponent implements OnInit, OnChanges {
     @Output() onSubmit: EventEmitter<Image> = new EventEmitter();
 
     public form = new FormGroup({
+        label: new FormControl('', [Validators.required]),
         file_path: new FormControl('', [Validators.required]),
         file_size: new FormControl('', [Validators.required]),
         ra_min: new FormControl('', [Validators.required]),
diff --git a/client/src/app/admin/instance/dataset/components/image/image-list.component.html b/client/src/app/admin/instance/dataset/components/image/image-list.component.html
new file mode 100644
index 00000000..ab9c4ee7
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/image-list.component.html
@@ -0,0 +1,75 @@
+<app-add-image 
+    [instance]="instance"
+    [dataset]="dataset"
+    [files]="files"
+    [filesIsLoading]="filesIsLoading"
+    [filesIsLoaded]="filesIsLoaded"
+    [fitsImageLimits]="fitsImageLimits"
+    [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
+    [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
+    (loadRootDirectory)="loadRootDirectory.emit($event)"
+    (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
+    (add)="add.emit($event)">
+</app-add-image>
+
+<div class="row" *ngIf="imageList.length === 0">
+    <div class="col-12 lead text-center font-weight-bold">
+        No images available...
+    </div>
+</div>
+
+<div *ngIf="imageList.length > 0" class="table-responsive">
+    <table class="table table-striped" aria-describedby="Image list">
+        <thead>
+            <tr>
+                <th scope="col">ID</th>
+                <th scope="col">Label</th>
+                <th scope="col">File path</th>
+                <th scope="col">File size</th>
+                <th scope="col">Ra [min,max]</th>
+                <th scope="col">Dec [min,max]</th>
+                <th scope="col">Stretch</th>
+                <th scope="col">Pmin</th>
+                <th scope="col">Pmax</th>
+                <th scope="col">Edit</th>
+                <th scope="col">Delete</th>
+            </tr>
+        </thead>
+        <tbody>
+            <tr *ngFor="let image of imageList">
+                <td class="align-middle">{{ image.id }}</td>
+                <td class="align-middle">{{ image.label }}</td>
+                <td class="align-middle">{{ image.file_path }}</td>
+                <td class="align-middle">{{ image.file_size | formatFileSize:false }}</td>
+                <td class="align-middle">[{{ image.ra_min }},{{ image.ra_max }}]</td>
+                <td class="align-middle">[{{ image.dec_min }},{{ image.dec_max }}]</td>
+                <td class="align-middle">{{ image.stretch }}</td>
+                <td class="align-middle">{{ image.pmin }}</td>
+                <td class="align-middle">{{ image.pmax }}</td>
+                <td class="align-middle">
+                    <app-edit-image
+                        [image]="image"
+                        [instance]="instance"
+                        [dataset]="dataset"
+                        [files]="files"
+                        [filesIsLoading]="filesIsLoading"
+                        [filesIsLoaded]="filesIsLoaded"
+                        [fitsImageLimits]="fitsImageLimits"
+                        [fitsImageLimitsIsLoading]="fitsImageLimitsIsLoading"
+                        [fitsImageLimitsIsLoaded]="fitsImageLimitsIsLoaded"
+                        (loadRootDirectory)="loadRootDirectory.emit($event)"
+                        (retrieveFitsImageLimits)="retrieveFitsImageLimits.emit($event)"
+                        (edit)="edit.emit($event)">
+                    </app-edit-image>
+                </td>
+                <td class="align-middle">
+                    <app-delete-btn
+                        [type]="'image'"
+                        [label]="image.file_path"
+                        (confirm)="delete.emit(image)">
+                    </app-delete-btn>
+                </td>
+            </tr>
+        </tbody>
+    </table>
+</div>
\ No newline at end of file
diff --git a/client/src/app/admin/instance/dataset/components/image/image-list.component.ts b/client/src/app/admin/instance/dataset/components/image/image-list.component.ts
new file mode 100644
index 00000000..0c1f87fe
--- /dev/null
+++ b/client/src/app/admin/instance/dataset/components/image/image-list.component.ts
@@ -0,0 +1,26 @@
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
+
+import { Dataset, Image, Instance } from 'src/app/metamodel/models';
+import { FileInfo, FitsImageLimits } from 'src/app/admin/store/models';
+
+@Component({
+    selector: 'app-image-list',
+    templateUrl: 'image-list.component.html',
+    changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class ImageListComponent {
+    @Input() imageList: Image[];
+    @Input() instance: Instance;
+    @Input() dataset: Dataset;
+    @Input() files: FileInfo[];
+    @Input() filesIsLoading: boolean;
+    @Input() filesIsLoaded: boolean;
+    @Input() fitsImageLimits: FitsImageLimits;
+    @Input() fitsImageLimitsIsLoading: boolean;
+    @Input() fitsImageLimitsIsLoaded: boolean;
+    @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
+    @Output() retrieveFitsImageLimits: EventEmitter<string> = new EventEmitter();
+    @Output() add: EventEmitter<Image> = new EventEmitter();
+    @Output() edit: EventEmitter<Image> = new EventEmitter();
+    @Output() delete: EventEmitter<Image> = new EventEmitter();
+}
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
deleted file mode 100644
index 7fca1eef..00000000
--- a/client/src/app/admin/instance/dataset/components/image/image-table.component.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<div class="mt-2 table-responsive">
-    <table class="table">
-        <thead>
-            <tr>
-                <th scope="col">File path</th>
-                <th scope="col">File size</th>
-                <th scope="col">Ra [min,max]</th>
-                <th scope="col">Dec [min,max]</th>
-                <th scope="col">Stretch</th>
-                <th scope="col">Pmin</th>
-                <th scope="col">Pmax</th>
-                <th scope="col">Edit</th>
-                <th scope="col">Delete</th>
-            </tr>
-        </thead>
-        <tbody>
-            <tr *ngFor="let image of imageList">
-                <td class="align-middle">{{ image.file_path }}</td>
-                <td class="align-middle">{{ image.file_size | formatFileSize:false }}</td>
-                <td class="align-middle">[{{ image.ra_min }},{{ image.ra_max }}]</td>
-                <td class="align-middle">[{{ image.dec_min }},{{ image.dec_max }}]</td>
-                <td class="align-middle">{{ image.stretch }}</td>
-                <td class="align-middle">{{ image.pmin }}</td>
-                <td class="align-middle">{{ image.pmax }}</td>
-                <td class="align-middle">
-                    <a title="Edit this image"
-                        routerLink="/admin/instance/configure-instance/{{ instance.name }}/dataset/configure-dataset/{{ dataset.name }}/edit-image/{{ image.id }}" 
-                        class="btn btn-outline-primary">
-                        <span class="fas fa-edit"></span>
-                    </a>
-                </td>
-                <td class="align-middle">
-                    <app-delete-btn
-                        [type]="'image'"
-                        [label]="image.file_path"
-                        (confirm)="deleteImage.emit(image)">
-                    </app-delete-btn>
-                </td>
-            </tr>
-        </tbody>
-    </table>
-</div>
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
deleted file mode 100644
index 669b4306..00000000
--- a/client/src/app/admin/instance/dataset/components/image/image-table.component.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * 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, Input, Output, ChangeDetectionStrategy, EventEmitter } from '@angular/core';
-
-import { Dataset, Image, Instance } from 'src/app/metamodel/models';
-
-@Component({
-    selector: 'app-image-table',
-    templateUrl: 'image-table.component.html',
-    changeDetection: ChangeDetectionStrategy.OnPush
-})
-export class ImageTableComponent {
-    @Input() instance: Instance;
-    @Input() dataset: Dataset;
-    @Input() imageList: Image[];
-    @Output() deleteImage: EventEmitter<Image> = new EventEmitter();
-}
diff --git a/client/src/app/admin/instance/dataset/components/image/index.ts b/client/src/app/admin/instance/dataset/components/image/index.ts
index 61a1e575..5c05c635 100644
--- a/client/src/app/admin/instance/dataset/components/image/index.ts
+++ b/client/src/app/admin/instance/dataset/components/image/index.ts
@@ -1,7 +1,11 @@
+import { ImageListComponent } from './image-list.component';
 import { ImageFormComponent } from './image-form.component';
-import { ImageTableComponent } from './image-table.component';
+import { AddImageComponent } from './add-image.component';
+import { EditImageComponent } from './edit-image.component';
 
 export const imageComponents = [
+    ImageListComponent,
     ImageFormComponent,
-    ImageTableComponent
+    AddImageComponent,
+    EditImageComponent
 ];
diff --git a/client/src/app/admin/instance/dataset/components/output-category/add-output-category.component.html b/client/src/app/admin/instance/dataset/components/output-category/add-output-category.component.html
index d1b223f1..e10aff74 100644
--- a/client/src/app/admin/instance/dataset/components/output-category/add-output-category.component.html
+++ b/client/src/app/admin/instance/dataset/components/output-category/add-output-category.component.html
@@ -1,6 +1,6 @@
 <div class="row mb-1">
     <div class="col-12">
-        <button title="Add new output category" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
+        <button title="Add new output category" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
             <span class="fas fa-plus"></span> New output category
         </button>
     </div>
diff --git a/client/src/app/admin/instance/dataset/components/output-family/add-output-family.component.html b/client/src/app/admin/instance/dataset/components/output-family/add-output-family.component.html
index 4e1cdf65..21451b32 100644
--- a/client/src/app/admin/instance/dataset/components/output-family/add-output-family.component.html
+++ b/client/src/app/admin/instance/dataset/components/output-family/add-output-family.component.html
@@ -1,6 +1,6 @@
 <div class="row mb-1">
     <div class="col-12">
-        <button title="Add new output family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success float-right">
+        <button title="Add new output family" (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-success">
             <span class="fas fa-plus"></span> New output family
         </button>
     </div>
diff --git a/client/src/app/admin/instance/dataset/containers/attribute-list.component.html b/client/src/app/admin/instance/dataset/containers/attribute-list.component.html
index db6e7d37..653c03e6 100644
--- a/client/src/app/admin/instance/dataset/containers/attribute-list.component.html
+++ b/client/src/app/admin/instance/dataset/containers/attribute-list.component.html
@@ -3,7 +3,8 @@
     || (criteriaFamilyListIsLoading | async)
     || (outputFamilyListIsLoading | async)
     || (outputCategoryListIsLoading | async)
-    || (selectOptionListIsLoading | async)">
+    || (selectOptionListIsLoading | async)
+    || (imageListIsLoading | async)">
 </app-spinner>
 
 <div *ngIf="(attributeListIsLoaded | async) 
@@ -11,7 +12,8 @@
     && (criteriaFamilyListIsLoaded | async)
     && (outputFamilyListIsLoaded | async)
     && (outputCategoryListIsLoaded | async)
-    && (selectOptionListIsLoaded | async)" class="container-fluid">
+    && (selectOptionListIsLoaded | async)
+    && (imageListIsLoaded | async)" class="container-fluid">
     <nav aria-label="breadcrumb">
         <ol class="breadcrumb">
             <li class="breadcrumb-item">
@@ -39,11 +41,6 @@
                     (loadColumnList)="loadColumnList()"
                     (add)="addAttribute($event)">
                 </app-add-attribute>
-                <div class="btn-group mr-2" role="group" aria-label="Second group">
-                    <button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/image-list" title="Images" class="btn btn-outline-primary">
-                        <span class="fas fa-image"></span> Images
-                    </button>
-                </div>
                 <div class="btn-group mr-2" role="group" aria-label="Third group">
                     <button routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/configure-cone-search" title="Configure cone-search" class="btn btn-outline-primary">
                         <span class="fas fa-search"></span> Configure cone-search
@@ -87,6 +84,9 @@
                         <li class="nav-item">
                             <a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'ocategories'}" [ngClass]="{'active': (tabSelected | async) === 'ocategories'}">Output Categories</a>
                         </li>
+                        <li class="nav-item">
+                            <a class="nav-link" routerLink="./" [queryParams]="{tab_selected: 'images'}" [ngClass]="{'active': (tabSelected | async) === 'images'}">Images</a>
+                        </li>
                     </ul>
                 </div>
                 <div class="card-body" [ngSwitch]="tabSelected | async">
@@ -166,6 +166,23 @@
                         (edit)="editOutputCategory($event)"
                         (delete)="deleteOutputCategory($event)">
                     </app-output-category-list>
+                    <app-image-list
+                        *ngSwitchCase="'images'"
+                        [imageList]="imageList | 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)"
+                        (add)="addImage($event)"
+                        (edit)="editImage($event)"
+                        (delete)="deleteImage($event)">
+                    </app-image-list>
                 </div>
             </div>
         </div>
diff --git a/client/src/app/admin/instance/dataset/containers/attribute-list.component.ts b/client/src/app/admin/instance/dataset/containers/attribute-list.component.ts
index 203ddbd0..45f162c6 100644
--- a/client/src/app/admin/instance/dataset/containers/attribute-list.component.ts
+++ b/client/src/app/admin/instance/dataset/containers/attribute-list.component.ts
@@ -14,8 +14,8 @@ import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 import { Store } from '@ngrx/store';
 
-import { SelectOption, Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily } from 'src/app/metamodel/models';
-import { Column } from 'src/app/admin/store/models';
+import { SelectOption, Instance, Dataset, Attribute, CriteriaFamily, OutputCategory, OutputFamily, Image } from 'src/app/metamodel/models';
+import { Column, 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 attributeActions from 'src/app/metamodel/actions/attribute.actions';
@@ -31,6 +31,12 @@ import * as columnActions from 'src/app/admin/store/actions/column.actions';
 import * as columnSelector from 'src/app/admin/store/selectors/column.selector';
 import * as attributeDistinctActions from 'src/app/admin/store/actions/attribute-distinct.actions';
 import * as attributeDistinctSelector from 'src/app/admin/store/selectors/attribute-distinct.selector';
+import * as imageActions from 'src/app/metamodel/actions/image.actions';
+import * as imageSelector from 'src/app/metamodel/selectors/image.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 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-attribute-list',
@@ -64,6 +70,15 @@ export class AttributeListComponent implements OnInit {
     public attributeDistinctList: Observable<string[]>;
     public attributeDistinctListIsLoading: Observable<boolean>;
     public attributeDistinctListIsLoaded: Observable<boolean>;
+    public imageList: Observable<Image[]>;
+    public imageListIsLoading: Observable<boolean>;
+    public imageListIsLoaded: 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<{ }>, private route: ActivatedRoute) {
         this.instance = store.select(instanceSelector.selectInstanceByRouteName);
@@ -91,6 +106,16 @@ export class AttributeListComponent implements OnInit {
         this.attributeDistinctList = store.select(attributeDistinctSelector.selectAllAttributeDistincts);
         this.attributeDistinctListIsLoading = store.select(attributeDistinctSelector.selectAttributeDistinctListIsLoading);
         this.attributeDistinctListIsLoaded = store.select(attributeDistinctSelector.selectAttributeDistinctListIsLoaded);
+        this.imageList = store.select(imageSelector.selectAllImages);
+        this.imageListIsLoading = store.select(imageSelector.selectImageListIsLoading);
+        this.imageListIsLoaded = store.select(imageSelector.selectImageListIsLoaded);
+        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);
+
     }
 
     ngOnInit() {
@@ -158,4 +183,24 @@ export class AttributeListComponent implements OnInit {
     deleteAttribute(attribute: Attribute) {
         this.store.dispatch(attributeActions.deleteAttribute({ attribute }));
     }
+
+    loadRootDirectory(path: string) {
+        this.store.dispatch(adminFileExplorerActions.loadFiles({ path }));
+    }
+
+    retrieveFitsImageLimits(filePath: string) {
+        this.store.dispatch(fitsImageActions.retrieveFitsImageLimits({ filePath }));
+    }
+
+    addImage(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 }));
+    }
 }
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
deleted file mode 100644
index 8216b131..00000000
--- a/client/src/app/admin/instance/dataset/containers/edit-image.component.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<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/configure-dataset/{{ (dataset | async).name }}" [queryParams]="{tab_selected: 'design'}">
-                    Configure dataset {{ (dataset | async).label }}
-                </a>
-            </li>
-            <li class="breadcrumb-item">
-                <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/image-list">
-                    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>
-                &nbsp;
-                <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
deleted file mode 100644
index a379464c..00000000
--- a/client/src/app/admin/instance/dataset/containers/edit-image.component.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * 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);
-    }
-
-    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
deleted file mode 100644
index 937986c0..00000000
--- a/client/src/app/admin/instance/dataset/containers/image-list.component.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<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">
-                <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}" [queryParams]="{tab_selected: 'design'}">
-                    Configure dataset {{ (dataset | async).label }}
-                </a>
-            </li>
-            <li class="breadcrumb-item active" aria-current="page">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/configure-dataset/{{ (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
deleted file mode 100644
index b0b005cd..00000000
--- a/client/src/app/admin/instance/dataset/containers/image-list.component.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/**
- * 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 { 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 {
-    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);
-    }
-
-    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
deleted file mode 100644
index bebd037b..00000000
--- a/client/src/app/admin/instance/dataset/containers/new-image.component.html
+++ /dev/null
@@ -1,51 +0,0 @@
-<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/configure-dataset/{{ (dataset | async).name }}" [queryParams]="{tab_selected: 'design'}">
-                    Configure dataset {{ (dataset | async).label }}
-                </a>
-            </li>
-            <li class="breadcrumb-item">
-                <a routerLink="/admin/instance/configure-instance/{{ (instance | async).name }}/dataset/configure-dataset/{{ (dataset | async).name }}/image-list">
-                    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>
-                &nbsp;
-                <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
deleted file mode 100644
index 5dd243e6..00000000
--- a/client/src/app/admin/instance/dataset/containers/new-image.component.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * 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 3ea9a9ac..ec84e5ad 100644
--- a/client/src/app/admin/instance/dataset/dataset-routing.module.ts
+++ b/client/src/app/admin/instance/dataset/dataset-routing.module.ts
@@ -15,9 +15,6 @@ import { NewDatasetComponent } from './containers/new-dataset.component';
 import { EditDatasetComponent } from './containers/edit-dataset.component';
 import { ConfigureDatasetComponent } from './containers/configure-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';
 import { ConfigureConeSearchComponent } from './containers/configure-cone-search.component';
 
 const routes: Routes = [
@@ -27,9 +24,6 @@ const routes: Routes = [
     { path: 'configure-dataset/:dname', component: ConfigureDatasetComponent, children:
         [
             { path: '', component: AttributeListComponent },
-            { path: 'image-list', component: ImageListComponent },
-            { path: 'new-image', component: NewImageComponent },
-            { path: 'edit-image/:id', component: EditImageComponent },
             { path: 'configure-cone-search', component: ConfigureConeSearchComponent }
         ]
     }
@@ -51,8 +45,5 @@ export const routedComponents = [
     EditDatasetComponent,
     ConfigureDatasetComponent,
     AttributeListComponent,
-    ImageListComponent,
-    NewImageComponent,
-    EditImageComponent,
     ConfigureConeSearchComponent
 ];
diff --git a/client/src/app/core/containers/app.component.html b/client/src/app/core/containers/app.component.html
index 77e2f506..9238c985 100644
--- a/client/src/app/core/containers/app.component.html
+++ b/client/src/app/core/containers/app.component.html
@@ -9,7 +9,10 @@
             &copy; ANIS 2014 - {{ year }}
         </div>
         <div class="row justify-content-center font-weight-bold mb-4">
-            Currently based on anis-client v{{ anisClientVersion }}. Code licensed CeCILL.
+            Currently based on ANIS v{{ anisClientVersion }}.
+        </div>
+        <div class="row justify-content-center font-weight-bold mb-4">
+            Code licensed CeCILL.
         </div>
         <div class="row justify-content-around">
             <div class="col mb-3 text-center">
diff --git a/client/src/app/instance/search/components/result/image-list-result.component.html b/client/src/app/instance/search/components/result/image-list-result.component.html
index f6f9ef6d..bc6939f8 100644
--- a/client/src/app/instance/search/components/result/image-list-result.component.html
+++ b/client/src/app/instance/search/components/result/image-list-result.component.html
@@ -2,7 +2,8 @@
 
 <div *ngIf="imageListIsLoaded" class="row">
     <div *ngFor="let image of imageList" class="col-2">
-        <div class="row">
+        <div class="row justify-content-center">
+            {{ image.label }}
             <span class="pointer">
                 <img (click)="openConeSearch(cs, getHref(image))" [src]="getHref(image)" height="100px" class="img-thumbnail mb-2" />
             </span>
diff --git a/client/src/app/metamodel/effects/image.effects.ts b/client/src/app/metamodel/effects/image.effects.ts
index cc97e74a..5bc60f90 100644
--- a/client/src/app/metamodel/effects/image.effects.ts
+++ b/client/src/app/metamodel/effects/image.effects.ts
@@ -8,7 +8,6 @@
  */
 
 import { Injectable } from '@angular/core';
-import { Router } from '@angular/router';
 
 import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects';
 import { Store } from '@ngrx/store';
@@ -19,7 +18,6 @@ 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
@@ -27,7 +25,6 @@ import * as instanceSelector from '../selectors/instance.selector';
  */
 @Injectable()
 export class ImageEffects {
-
     /**
      * Calls action to retrieve image list.
      */
@@ -66,14 +63,7 @@ export class ImageEffects {
     addImageSuccess$ = createEffect(() =>
         this.actions$.pipe(
             ofType(imageActions.addImageSuccess),
-            concatLatestFrom(() => [
-                this.store.select(instanceSelector.selectInstanceNameByRoute),
-                this.store.select(datasetSelector.selectDatasetNameByRoute)
-            ]),
-            tap(([, instanceName, datasetName]) => {
-                this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/dataset/configure-dataset/${datasetName}/image-list`);
-                this.toastr.success('Image successfully added', 'The new image was added into the database');
-            })
+            tap(() => this.toastr.success('Image successfully added', 'The new image was added into the database'))
         ), { dispatch: false }
     );
 
@@ -108,14 +98,7 @@ export class ImageEffects {
     editImageSuccess$ = createEffect(() =>
         this.actions$.pipe(
             ofType(imageActions.editImageSuccess),
-            concatLatestFrom(() => [
-                this.store.select(instanceSelector.selectInstanceNameByRoute),
-                this.store.select(datasetSelector.selectDatasetNameByRoute)
-            ]),
-            tap(([, instanceName, datasetName]) => {
-                this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/dataset/configure-dataset/${datasetName}/image-list`);
-                this.toastr.success('Image successfully edited', 'The existing image has been edited into the database');
-            })
+            tap(() => this.toastr.success('Image successfully edited', 'The existing image has been edited into the database'))
         ), { dispatch: false }
     );
 
@@ -167,7 +150,6 @@ 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/models/image.model.ts b/client/src/app/metamodel/models/image.model.ts
index e3184ac3..9a603ec1 100644
--- a/client/src/app/metamodel/models/image.model.ts
+++ b/client/src/app/metamodel/models/image.model.ts
@@ -14,6 +14,7 @@
  */
 export interface Image {
     id: number;
+    label: string;
     file_path: string;
     file_size: number;
     ra_min: number;
diff --git a/server/doctrine-proxy/__CG__AppEntityImage.php b/server/doctrine-proxy/__CG__AppEntityImage.php
index 31bc931b..2d92aa5d 100644
--- a/server/doctrine-proxy/__CG__AppEntityImage.php
+++ b/server/doctrine-proxy/__CG__AppEntityImage.php
@@ -67,10 +67,10 @@ class Image extends \App\Entity\Image implements \Doctrine\ORM\Proxy\Proxy
     public function __sleep()
     {
         if ($this->__isInitialized__) {
-            return ['__isInitialized__', 'id', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
+            return ['__isInitialized__', 'id', 'label', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
         }
 
-        return ['__isInitialized__', 'id', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
+        return ['__isInitialized__', 'id', 'label', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
     }
 
     /**
@@ -192,6 +192,28 @@ class Image extends \App\Entity\Image implements \Doctrine\ORM\Proxy\Proxy
         return parent::getId();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function getLabel()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getLabel', []);
+
+        return parent::getLabel();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setLabel($label)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setLabel', [$label]);
+
+        return parent::setLabel($label);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/server/src/Action/ImageAction.php b/server/src/Action/ImageAction.php
index b1646dd1..68b13e1b 100644
--- a/server/src/Action/ImageAction.php
+++ b/server/src/Action/ImageAction.php
@@ -63,6 +63,7 @@ final class ImageAction extends AbstractAction
             $parsedBody = $request->getParsedBody();
 
             $fields = array(
+                'label',
                 'file_path',
                 'file_size',
                 'ra_min',
@@ -108,6 +109,7 @@ final class ImageAction extends AbstractAction
      */
     private function editImage(Image $image, array $parsedBody): void
     {
+        $image->setLabel($parsedBody['label']);
         $image->setFilePath($parsedBody['file_path']);
         $image->setFileSize($parsedBody['file_size']);
         $image->setRaMin($parsedBody['ra_min']);
diff --git a/server/src/Action/ImageListAction.php b/server/src/Action/ImageListAction.php
index 09a5579b..9123ca00 100644
--- a/server/src/Action/ImageListAction.php
+++ b/server/src/Action/ImageListAction.php
@@ -66,6 +66,7 @@ final class ImageListAction extends AbstractAction
             $parsedBody = $request->getParsedBody();
 
             $fields = array(
+                'label',
                 'file_path',
                 'file_size',
                 'ra_min',
@@ -105,6 +106,7 @@ final class ImageListAction extends AbstractAction
     private function postImage(array $parsedBody, Dataset $dataset): Image
     {
         $image = new Image($dataset);
+        $image->setLabel($parsedBody['label']);
         $image->setFilePath($parsedBody['file_path']);
         $image->setFileSize($parsedBody['file_size']);
         $image->setRaMin($parsedBody['ra_min']);
diff --git a/server/src/Entity/Image.php b/server/src/Entity/Image.php
index ab55065f..fb649dc0 100644
--- a/server/src/Entity/Image.php
+++ b/server/src/Entity/Image.php
@@ -30,6 +30,13 @@ class Image implements \JsonSerializable
      */
     protected $id;
 
+    /**
+     * @var string
+     *
+     * @Column(type="string", name="label", nullable=false)
+     */
+    protected $label;
+
     /**
      * @var string
      *
@@ -111,6 +118,16 @@ class Image implements \JsonSerializable
         return $this->id;
     }
 
+    public function getLabel()
+    {
+        return $this->label;
+    }
+
+    public function setLabel($label)
+    {
+        $this->label = $label;
+    }
+
     public function getFilePath()
     {
         return $this->filePath;
@@ -210,6 +227,7 @@ class Image implements \JsonSerializable
     {
         return [
             'id' => $this->getId(),
+            'label' => $this->getLabel(),
             'file_path' => $this->getFilePath(),
             'file_size' => $this->getFileSize(),
             'ra_min' => $this->getRaMin(),
-- 
GitLab