You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datalab.apache.org by hs...@apache.org on 2022/07/14 08:45:47 UTC

[incubator-datalab] 01/01: [DATALAB-2883] finished modal info window about images

This is an automated email from the ASF dual-hosted git repository.

hshpak pushed a commit to branch feat/DATALAB-2883/view-additional-info-about-images
in repository https://gitbox.apache.org/repos/asf/incubator-datalab.git

commit 74ff6711c1dfa96a4364189dd9ec89a4c332dec1
Author: Hennadii_Shpak <bo...@gmail.com>
AuthorDate: Fri Jul 8 13:00:11 2022 +0300

    [DATALAB-2883] finished modal info window about images
---
 .../pipes/library-name-normalize/index.ts}         | 15 ++--
 .../library-name-normalize.pipe.ts}                | 29 ++++----
 .../pipes/truncate-text-pipe/truncate-text.pipe.ts |  8 +--
 .../webapp/src/app/core/util/sortUtils.ts          |  2 +-
 .../detail-dialog/detail-dialog.component.html     | 68 +++++++++---------
 .../detail-dialog/detail-dialog.component.ts       | 10 +--
 .../image-detail-dialog.component.html             | 83 ++++++++++++++++++++++
 .../image-detail-dialog.component.scss}            | 56 +++++++++++----
 .../image-detail-dialog.component.ts               | 50 +++++++++++++
 .../image-detail-dialog.module.ts}                 | 23 ++++--
 .../library-info-modal.component.html}             | 22 ++----
 .../library-info-modal.component.scss}             | 20 +-----
 .../library-info-modal.component.ts}               | 29 ++++----
 .../library-info-modal.module.ts}                  | 15 ++--
 .../share-image/share-image-dialog.component.html} |  0
 .../share-image/share-image-dialog.component.scss} |  0
 .../share-image/share-image-dialog.component.ts}   | 15 ++--
 .../share-image/share-image-dialog.module.ts}      | 12 ++--
 .../src/app/resources/images/images.component.html |  6 +-
 .../src/app/resources/images/images.component.ts   | 24 +++----
 .../src/app/resources/images/images.model.ts       | 23 ++++++
 .../webapp/src/app/resources/resources.module.ts   |  8 ++-
 .../src/main/resources/webapp/src/styles.scss      |  9 +++
 23 files changed, 355 insertions(+), 172 deletions(-)

diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/index.ts
similarity index 74%
copy from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/index.ts
index bd7a99d1d..48f7e5a22 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/index.ts
@@ -19,15 +19,12 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { ShareImageComponent } from './share-image.component';
-import { NotificationDialogComponent } from '../notification-dialog';
-
-
+import { LibraryNameNormalizePipe } from './library-name-normalize.pipe';
 
 @NgModule({
-  declarations: [ ShareImageComponent ],
-  imports: [ CommonModule ],
-  entryComponents: [ShareImageComponent],
-  exports: [ ShareImageComponent ]
+  imports: [CommonModule],
+  declarations: [LibraryNameNormalizePipe],
+  exports: [LibraryNameNormalizePipe]
 })
-export class ShareImageModule { }
+
+export class LibraryNameNormalizePipeModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/library-name-normalize.pipe.ts
similarity index 66%
copy from services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
copy to services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/library-name-normalize.pipe.ts
index f497deecc..3782c3a8e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/library-name-normalize/library-name-normalize.pipe.ts
@@ -17,20 +17,23 @@
  * under the License.
  */
 
-import { Pipe, PipeTransform } from "@angular/core";
+import {Pipe, PipeTransform} from '@angular/core';
 
-const MAX_SYMBOLS_COUNT = 255;
+const  libNameList = {
+  os_pkg: 'Apt/Yum',
+  pip3: 'Python 3',
+  r_pkg: 'R packages',
+  java: 'Java',
+  others: 'Others'
+};
 
-@Pipe({
-    name: 'truncateTextPipe'
-})
-export class TruncateTextPipe implements PipeTransform {
-    transform(text: string, limit: number = MAX_SYMBOLS_COUNT): string {
-        if (!text) {
-            return ''
-        }
-        return text.length > limit 
-            ? `${text.substring(0, limit)}...`
-            : text
+@Pipe({name: 'libNameNormalize'})
+
+export class LibraryNameNormalizePipe implements PipeTransform {
+  transform(libGroupName: string): string {
+    if (!libGroupName) {
+      return '';
     }
+    return libNameList[libGroupName];
+  }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
index f497deecc..28ddf9e3d 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-import { Pipe, PipeTransform } from "@angular/core";
+import { Pipe, PipeTransform } from '@angular/core';
 
 const MAX_SYMBOLS_COUNT = 255;
 
@@ -27,10 +27,10 @@ const MAX_SYMBOLS_COUNT = 255;
 export class TruncateTextPipe implements PipeTransform {
     transform(text: string, limit: number = MAX_SYMBOLS_COUNT): string {
         if (!text) {
-            return ''
+            return '';
         }
-        return text.length > limit 
+        return text.length > limit
             ? `${text.substring(0, limit)}...`
-            : text
+            : text;
     }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts b/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
index fa220dacf..9e06ec163 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/core/util/sortUtils.ts
@@ -56,7 +56,7 @@ export class SortUtils {
   }
 
   public static flatDeep(arr, d = 1) {
-    return d > 0 
+    return d > 0
       ? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? this.flatDeep(val, d - 1) : val), [])
       : arr.slice();
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
index 3397676b2..80c11d635 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.html
@@ -68,15 +68,15 @@
           <!--              </a>-->
           <!--            </p> -->
             <ng-container *ngFor="let item of notebook.exploratory_urls">
-              <span 
-                class="d-none" 
-                *ngIf="item.description.toLowerCase() === 'ungit' 
+              <span
+                class="d-none"
+                *ngIf="item.description.toLowerCase() === 'ungit'
                   && notebook.exploratory_urls[0].description.toLowerCase().indexOf('zeppelin') !== -1; else ungit"
               ></span>
               <ng-template #ungit>
                 <p (mouseleave)="hideCopyIcon()">
                   <span class="description">{{item.description}}: &nbsp;</span>
-                  <a 
+                  <a
                     (mouseover)="showCopyIcon(item.description)"
                     (click)="logAction(notebook.name, item.description)"
                     class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
@@ -87,11 +87,11 @@
                   >
                     &nbsp;{{item.url | truncateTextPipe: urlMaxLength}}
                   </a>
-                  <span 
-                    (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()" 
-                    *ngIf="isCopyIconVissible[item.description]" 
-                    [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'" 
-                    matTooltipPosition="above" 
+                  <span
+                    (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()"
+                    *ngIf="isCopyIconVissible[item.description]"
+                    [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'"
+                    matTooltipPosition="above"
                     class="copy-icon-wrapper"
                   >
                     <span  class="link-icon" (click)="copyLink(item.url)" >
@@ -105,22 +105,22 @@
         </div>
         <div class="scroll-box" id="scrolling" *ngIf="data.type === 'resource'">
           <div class="detail-info" *ngIf="!notebook.error_message">
-            <p>Edge Node IP Address {{notebook.node_ip}}</p>
+            <p>Edge Node IP Address {{notebook.node_ip}}  </p>
             <p *ngIf="notebook.status === 'running'">Up time {{upTimeInHours}} hour(s) since
               {{notebook.time ? (notebook.time | longDate) : "not specified."}}
             </p>
             <p *ngIf="notebook.url?.length">Open following URL(s) in your browser to access this box:</p>
             <div class="links_block">
               <ng-container *ngFor="let item of notebook.url">
-                <span 
-                  class="d-none" 
-                  *ngIf="item.description.toLowerCase() === 'ungit' 
+                <span
+                  class="d-none"
+                  *ngIf="item.description.toLowerCase() === 'ungit'
                     && notebook.template_name.toLowerCase().indexOf('zeppelin ') !== -1; else ungit"
                 ></span>
                 <ng-template #ungit>
                   <p (mouseleave)="hideCopyIcon()">
                     <span class="description">{{item.description}}: &nbsp;</span>
-                    <a 
+                    <a
                       (mouseover)="showCopyIcon(item.description)"
                       (click)="logAction(notebook.name, item.description)"
                       class="ellipsis none-select resources-url" matTooltip="{{item.url}}"
@@ -131,11 +131,11 @@
                     >
                       &nbsp;{{item.url | truncateTextPipe: urlMaxLength}}
                     </a>
-                    <span 
-                      (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()" 
-                      *ngIf="isCopyIconVissible[item.description]" 
-                      [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'" 
-                      matTooltipPosition="above" 
+                    <span
+                      (click)="logAction(notebook.name, item.description, 'Copy');$event.stopPropagation()"
+                      *ngIf="isCopyIconVissible[item.description]"
+                      [matTooltip]="isCopied ? 'Copy ' + item.description + ' url': 'Copied'"
+                      matTooltipPosition="above"
                       class="copy-icon-wrapper"
                     >
                       <span  class="link-icon" (click)="copyLink(item.url)" >
@@ -199,7 +199,7 @@
               <!--                 [ngClass]="{'not-allow': !this.bucketStatus['view'] || !thisdata.buckets.length}"-->
               <!--                (click)="bucketBrowser(notebook.bucket_name, notebook.endpoint, this.bucketStatus['view'] && thisdata.buckets.length)"-->
               <!--              >-->
-              <span 
+              <span
                 class="description open-bucket"
                 [matTooltip]="!this.bucketStatus['view']
                   ? 'You have not permission to open bucket'
@@ -209,8 +209,8 @@
                 [matTooltipClass]="'full-size-tooltip'"
                 [ngClass]="{'not-allow': !this.bucketStatus['view'] || !this.data.buckets.length}"
                 (click)="bucketBrowser(
-                  notebook.cloud_provider !== 'azure' ? notebook.bucket_name : notebook.account_name + '.' + notebook.bucket_name, 
-                  notebook.endpoint, 
+                  notebook.cloud_provider !== 'azure' ? notebook.bucket_name : notebook.account_name + '.' + notebook.bucket_name,
+                  notebook.endpoint,
                   this.bucketStatus['view'] && this.data.buckets.length
                 )"
               >
@@ -265,10 +265,10 @@
                           </p> -->
           </div>
 
-          <div 
-            class="checkbox-group" 
+          <div
+            class="checkbox-group"
             *ngIf="notebook.image !== 'docker.datalab-zeppelin'; else not_support"
-            [hidden]="notebook.status !== 'running' || notebook.image === 'docker.datalab-superset' 
+            [hidden]="notebook.status !== 'running' || notebook.image === 'docker.datalab-superset'
               || notebook.image === 'docker.datalab-jupyterlab'"
           >
             <label>
@@ -277,15 +277,15 @@
             <div class="checkbox-group">
               <form [formGroup]="configurationForm" novalidate>
                 <div class="config-details" *ngIf="configuration?.nativeElement['checked'] || false">
-                  <textarea 
-                    formControlName="configuration_parameters" 
+                  <textarea
+                    formControlName="configuration_parameters"
                     id="config"
-                    placeholder="Cluster configuration template, JSON" 
+                    placeholder="Cluster configuration template, JSON"
                     data-gramm_editor="false"
                   ></textarea>
-                  <span 
+                  <span
                     class="danger_color"
-                    *ngIf="!configurationForm.controls.configuration_parameters.valid 
+                    *ngIf="!configurationForm.controls.configuration_parameters.valid
                       && configurationForm.controls['configuration_parameters'].dirty"
                   >
                     Configuration parameters is not in a valid format
@@ -320,10 +320,10 @@
             <div *ngFor="let url of odahu.url" class="odahu-links">
               <div class="odahu-link">
                 <span class="description">{{url.description }}: &nbsp;</span>
-                <a 
-                  class="ellipsis" 
-                  matTooltip="{{ url.url}}" 
-                  matTooltipPosition="above" 
+                <a
+                  class="ellipsis"
+                  matTooltip="{{ url.url}}"
+                  matTooltipPosition="above"
                   href="{{ url.url}}"
                   target="_blank"
                 >
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
index 15845a6b2..c855f765a 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/detail-dialog/detail-dialog.component.ts
@@ -151,10 +151,10 @@ export class DetailDialogComponent implements OnInit {
     bucketName = this.isBucketAllowed ? bucketName : this.data.buckets[0].children[0].name;
     // bucketName = 'ofuks-1304-pr2-local-bucket';
     this.dialog.open(BucketBrowserComponent, { data:
-      { 
-        bucket: bucketName, 
-        endpoint: endpoint, 
-        bucketStatus: this.bucketStatus, 
+      {
+        bucket: bucketName,
+        endpoint: endpoint,
+        bucketStatus: this.bucketStatus,
         buckets: this.data.buckets
       },
       panelClass: 'modal-fullscreen' }
@@ -164,7 +164,7 @@ export class DetailDialogComponent implements OnInit {
   public showCopyIcon(element) {
     this.isCopyIconVissible[element] = true;
   }
-  
+
   public hideCopyIcon() {
     for (const key in this.isCopyIconVissible) {
       this.isCopyIconVissible[key] = false;
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.html
new file mode 100644
index 000000000..70262a064
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.html
@@ -0,0 +1,83 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<div class="detail-dialog" id="dialog-box">
+  <header class="dialog-header header-white">
+    <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
+  </header>
+
+  <div>
+    <table class="detail-header">
+      <tr>
+        <td>{{ data.image.name }}</td>
+        <td>{{ data.image.project }}</td>
+        <td>{{ data.image.instanceName }}</td>
+        <td>
+            <span class="status" [ngClass]="data.image.status.toLowerCase()">
+              {{ data.image.status | titlecase }}
+            </span>
+        </td>
+      </tr>
+    </table>
+
+    <div class="content-box">
+      <div *ngIf="data.image.description" class="image__description--wrapper">
+        <p
+          [matTooltip]="data.image.description"
+          matTooltipPosition="above"
+          [matTooltipDisabled]="data.image.description.length < maxDescriptionLength"
+          class="image__description">
+          {{ data.image.description | truncateTextPipe : maxDescriptionLength}}
+        </p>
+      </div>
+
+      <div class="image__template--wrapper">
+        <span class="modal-row__item modal-row__item--title">Template name</span>
+        <span class="modal-row__item">{{ data.image.application }}</span>
+      </div>
+
+      <div class="image__libraries--wrapper">
+        <span class="modal-row__item modal-row__item--title">Installed libraries` groups</span>
+        <div class="language__wrapper modal-row__item">
+          <div *ngIf="data.image.libraries.length; else notAvailable">
+            <div *ngFor="let library of data.image.libraries" class="library__wrapper">
+              <span>{{library.group | libNameNormalize}}</span>
+              <i (click)="onLibraryInfo(library)" class="material-icons library__info">info</i>
+            </div>
+          </div>
+          <ng-template #notAvailable>
+            <span class="no-libraries">No additional libraries installed</span>
+          </ng-template>
+        </div>
+      </div>
+      <div class="image__provider--wrapper">
+        <span class="modal-row__item modal-row__item--title">Provider</span>
+        <span class="modal-row__item">{{ data.image.cloudProvider }}</span>
+      </div>
+      <div class="image__creation-date--wrapper">
+        <span class="modal-row__item modal-row__item--title">Creation date</span>
+        <span class="modal-row__item">{{data.image.timestamp | localDate : 'short'}} </span>
+      </div>
+      <div class="image__creator--wrapper">
+        <span class="modal-row__item modal-row__item--title">Creator</span>
+        <span class="modal-row__item">{{ data.image.user }}</span>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.scss
similarity index 59%
copy from services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.scss
index f497deecc..c73b2d111 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.scss
@@ -17,20 +17,46 @@
  * under the License.
  */
 
-import { Pipe, PipeTransform } from "@angular/core";
-
-const MAX_SYMBOLS_COUNT = 255;
-
-@Pipe({
-    name: 'truncateTextPipe'
-})
-export class TruncateTextPipe implements PipeTransform {
-    transform(text: string, limit: number = MAX_SYMBOLS_COUNT): string {
-        if (!text) {
-            return ''
-        }
-        return text.length > limit 
-            ? `${text.substring(0, limit)}...`
-            : text
+.image__description {
+  font-size: 12px;
+  text-align: justify;
+}
+
+.content-box > div {
+  display: flex;
+  justify-content: space-between;
+  padding: 10px 0;
+}
+
+.content-box > div {
+  &:not(:last-child) {
+    border-bottom: 1px solid #edf1f5;
+  }
+}
+
+.modal-row__item {
+  width: 50%;
+
+  &--title {
+    font-weight: 500;
+  }
+}
+
+.library {
+  &__wrapper {
+    display: flex;
+    justify-content: space-between;
+
+    &:not(:last-child) {
+      margin-bottom: 10px;
     }
+  }
+
+  &__info {
+    cursor: pointer;
+  }
+}
+
+.no-libraries {
+  color: rgba(113, 138, 165, 0.5);
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
new file mode 100644
index 000000000..2659ee7d0
--- /dev/null
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.component.ts
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { Component, Inject, OnInit } from '@angular/core';
+import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material/dialog';
+import {Library, ModalData} from '../../images';
+import {LibraryInfoModalComponent} from '../library-info-modal/library-info-modal.component';
+
+@Component({
+  selector: 'datalab-image-detail-dialog',
+  templateUrl: './image-detail-dialog.component.html',
+  styleUrls: [
+    './image-detail-dialog.component.scss',
+    '../detail-dialog/detail-dialog.component.scss'
+  ]
+})
+
+export class ImageDetailDialogComponent {
+  maxDescriptionLength: number = 170;
+  constructor(
+    public dialogRef: MatDialogRef<ImageDetailDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: ModalData,
+    private dialog: MatDialog,
+  ) { }
+
+  onLibraryInfo(library: Library): void {
+    this.dialog.open(LibraryInfoModalComponent, {
+      data: {
+        library
+      },
+      panelClass: 'library-dialog-container'
+    });
+  }
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.module.ts
similarity index 55%
copy from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.module.ts
index bd7a99d1d..44a9cbc6c 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/image-detail-dialog/image-detail-dialog.module.ts
@@ -19,15 +19,24 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { ShareImageComponent } from './share-image.component';
-import { NotificationDialogComponent } from '../notification-dialog';
+import { ImageDetailDialogComponent } from './image-detail-dialog.component';
+import { TruncateTextPipeModule } from '../../../core/pipes/truncate-text-pipe';
+import {LocalDatePipeModule} from '../../../core/pipes/local-date-pipe';
+import {MatTooltipModule} from '@angular/material/tooltip';
+import {LibraryNameNormalizePipeModule} from '../../../core/pipes/library-name-normalize';
 
 
 
 @NgModule({
-  declarations: [ ShareImageComponent ],
-  imports: [ CommonModule ],
-  entryComponents: [ShareImageComponent],
-  exports: [ ShareImageComponent ]
+  declarations: [ ImageDetailDialogComponent ],
+    imports: [
+        CommonModule,
+        TruncateTextPipeModule,
+        LocalDatePipeModule,
+        MatTooltipModule,
+        LibraryNameNormalizePipeModule
+    ],
+  exports: [ ImageDetailDialogComponent ],
+  entryComponents: [ ImageDetailDialogComponent ]
 })
-export class ShareImageModule { }
+export class ImageDetailDialogModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.html
similarity index 61%
copy from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.html
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.html
index a6f1d1e2a..df0e9e5c1 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.html
@@ -17,23 +17,15 @@
   ~ under the License.
   -->
 
-<div id="dialog-box">
+<div class="detail-dialog" id="dialog-box">
   <header class="dialog-header">
-    <h4 class="modal-title">Share Image: <span>{{imageName}}</span></h4>
+    <h1 class="modal__title">Installed libraries</h1>
     <button type="button" class="close" (click)="dialogRef.close()">&times;</button>
   </header>
-  <section class="content">
-    <p class="description">
-      The image will be shared with all current Regular Users on the project with all the data and code.
-    </p>
-    <p class="question center">
-      Do you want proceed?
-    </p>
-    <div class="text-center m-top-30 m-bott-10">
-      <button type="button" class="butt mat-raised-button" (click)="dialogRef.close()">No</button>
-      <button type="button" class="butt butt-success mat-raised-button"
-              (click)="onShare()">Yes
-      </button>
+
+  <div class="content-box">
+    <div class="library__wrapper" *ngFor="let library of data.library.add_pkgs">
+      <span>{{library}}</span>
     </div>
-  </section>
+  </div>
 </div>
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.scss
similarity index 65%
copy from services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.scss
index f497deecc..e7e365b0f 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.scss
@@ -1,4 +1,4 @@
-/*
+/*!
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
  * distributed with this work for additional information
@@ -17,20 +17,6 @@
  * under the License.
  */
 
-import { Pipe, PipeTransform } from "@angular/core";
-
-const MAX_SYMBOLS_COUNT = 255;
-
-@Pipe({
-    name: 'truncateTextPipe'
-})
-export class TruncateTextPipe implements PipeTransform {
-    transform(text: string, limit: number = MAX_SYMBOLS_COUNT): string {
-        if (!text) {
-            return ''
-        }
-        return text.length > limit 
-            ? `${text.substring(0, limit)}...`
-            : text
-    }
+.modal__title {
+  font-size: 12px;
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.ts
similarity index 60%
copy from services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.ts
index f497deecc..e6c8d2c1e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/core/pipes/truncate-text-pipe/truncate-text.pipe.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.component.ts
@@ -17,20 +17,21 @@
  * under the License.
  */
 
-import { Pipe, PipeTransform } from "@angular/core";
+import {Component, Inject, OnInit} from '@angular/core';
+import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
 
-const MAX_SYMBOLS_COUNT = 255;
-
-@Pipe({
-    name: 'truncateTextPipe'
+@Component({
+  selector: 'datalab-library-info-modal',
+  templateUrl: './library-info-modal.component.html',
+  styleUrls: [
+    './library-info-modal.component.scss',
+    '../detail-dialog/detail-dialog.component.scss'
+  ]
 })
-export class TruncateTextPipe implements PipeTransform {
-    transform(text: string, limit: number = MAX_SYMBOLS_COUNT): string {
-        if (!text) {
-            return ''
-        }
-        return text.length > limit 
-            ? `${text.substring(0, limit)}...`
-            : text
-    }
+export class LibraryInfoModalComponent {
+
+  constructor(
+    public dialogRef: MatDialogRef<LibraryInfoModalComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: any,
+  ) { }
 }
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.module.ts
similarity index 74%
copy from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
copy to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.module.ts
index bd7a99d1d..80cd1f853 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/library-info-modal/library-info-modal.module.ts
@@ -19,15 +19,16 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { ShareImageComponent } from './share-image.component';
-import { NotificationDialogComponent } from '../notification-dialog';
+import {LibraryInfoModalComponent} from './library-info-modal.component';
 
 
 
 @NgModule({
-  declarations: [ ShareImageComponent ],
-  imports: [ CommonModule ],
-  entryComponents: [ShareImageComponent],
-  exports: [ ShareImageComponent ]
+  declarations: [LibraryInfoModalComponent],
+  imports: [
+    CommonModule
+  ],
+  exports: [LibraryInfoModalComponent],
+  entryComponents: [LibraryInfoModalComponent]
 })
-export class ShareImageModule { }
+export class LibraryInfoModalModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.html
similarity index 100%
rename from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.html
rename to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.html
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.scss b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.scss
similarity index 100%
rename from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.scss
rename to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.scss
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.ts
similarity index 78%
rename from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.ts
rename to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.ts
index 8afa9a088..25bd72c62 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.component.ts
@@ -19,23 +19,22 @@
 
 import { Component, Inject } from '@angular/core';
 import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
-import { ImagesService } from '../../../resources/images/images.service';
+import { ImagesService } from '../../images/images.service';
 import { UserImagesPageService } from '../../../core/services';
-import { Toaster_Message } from '../../../resources/images';
+import { ModalData, Toaster_Message } from '../../images';
 import { ToastrService } from 'ngx-toastr';
-import { tap } from 'rxjs/operators';
 
 @Component({
   selector: 'datalab-share-image',
-  templateUrl: './share-image.component.html',
-  styleUrls: ['./share-image.component.scss']
+  templateUrl: './share-image-dialog.component.html',
+  styleUrls: ['./share-image-dialog.component.scss']
 })
-export class ShareImageComponent {
+export class ShareImageDialogComponent {
   imageName!: string;
 
   constructor(
-    public dialogRef: MatDialogRef<ShareImageComponent>,
-    @Inject(MAT_DIALOG_DATA) public data: any,
+    public dialogRef: MatDialogRef<ShareImageDialogComponent>,
+    @Inject(MAT_DIALOG_DATA) public data: ModalData,
     private imagesService: ImagesService,
     private userImagesPageService: UserImagesPageService,
     private toastr: ToastrService,
diff --git a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.module.ts
similarity index 73%
rename from services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
rename to services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.module.ts
index bd7a99d1d..158aa0d09 100644
--- a/services/self-service/src/main/resources/webapp/src/app/shared/modal-dialog/share-image/share-image.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/exploratory/share-image/share-image-dialog.module.ts
@@ -19,15 +19,15 @@
 
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
-import { ShareImageComponent } from './share-image.component';
-import { NotificationDialogComponent } from '../notification-dialog';
+import { ShareImageDialogComponent } from './share-image-dialog.component';
+import { NotificationDialogComponent } from '../../../shared/modal-dialog/notification-dialog';
 
 
 
 @NgModule({
-  declarations: [ ShareImageComponent ],
+  declarations: [ ShareImageDialogComponent ],
   imports: [ CommonModule ],
-  entryComponents: [ShareImageComponent],
-  exports: [ ShareImageComponent ]
+  entryComponents: [ShareImageDialogComponent],
+  exports: [ ShareImageDialogComponent ]
 })
-export class ShareImageModule { }
+export class ShareImageDialogModule { }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
index 9f85994d0..0f9ea1c9e 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.html
@@ -227,9 +227,9 @@
       <td mat-cell *matCellDef="let element" class="settings actions-col">
 
         <div class="button--wrapper">
-          <span class="currency_details" >
-                <i class="material-icons">help_outline</i>
-              </span>
+          <span class="currency_details" (click)="onImageInfo(element)">
+            <i class="material-icons">help_outline</i>
+          </span>
           <span #settings class="actions" (click)="actions.toggle($event, settings)"></span>
         </div>
         <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left">
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
index afb464fa3..a5abe8513 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.component.ts
@@ -26,10 +26,11 @@ import { HealthStatusService, UserImagesPageService } from '../../core/services'
 import { ImageModel, ProjectModel, ShareImageAllUsersParams } from './images.model';
 import { Image_Table_Column_Headers, Image_Table_Titles, Localstorage_Key, Shared_Status, Toaster_Message } from './images.config';
 import { MatDialog } from '@angular/material/dialog';
-import { ShareImageComponent } from '../../shared/modal-dialog/share-image/share-image.component';
+import { ShareImageDialogComponent } from '../exploratory/share-image/share-image-dialog.component';
 import { Observable } from 'rxjs';
 import { ImagesService } from './images.service';
 import { ProgressBarService } from '../../core/services/progress-bar.service';
+import { ImageDetailDialogComponent } from '../exploratory/image-detail-dialog/image-detail-dialog.component';
 
 @Component({
   selector: 'datalab-images',
@@ -105,8 +106,17 @@ export class ImagesComponent implements OnInit {
     this.activeProjectName = '';
   }
 
+  onImageInfo(image: ImageModel): void {
+    this.dialog.open(ImageDetailDialogComponent, {
+      data: {
+        image
+      },
+      panelClass: 'modal-md'
+    });
+  }
+
   onShare(image: ImageModel): void {
-    this.dialog.open(ShareImageComponent, {
+    this.dialog.open(ShareImageDialogComponent, {
       data: {
         image
       },
@@ -159,16 +169,6 @@ export class ImagesComponent implements OnInit {
     this.userName = localStorage.getItem(Localstorage_Key.userName);
   }
 
-  private shareImageAllUsers(image: ImageModel): Observable<ProjectModel[]> {
-    const shareParams: ShareImageAllUsersParams = {
-      imageName: image.name,
-      projectName: image.project,
-      endpoint: image.endpoint
-    };
-
-    return this.userImagesPageService.shareImageAllUsers(shareParams);
-  }
-
   get isProjectsMoreThanOne(): boolean {
     return this.projectList.length > 1;
   }
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
index 8c0735950..24ce705bc 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/images/images.model.ts
@@ -17,6 +17,9 @@ export interface ImageModel {
   status: 'created' | 'creating' | 'terminated' | 'terminating' | 'failed';
   user: string;
   isSelected?: boolean;
+  libraries: Library[];
+  computationalLibraries: Library[];
+  clusterConfig: ClusterConfig;
 }
 
 export interface ShareImageAllUsersParams {
@@ -24,3 +27,23 @@ export interface ShareImageAllUsersParams {
   projectName: string;
   endpoint: string;
 }
+
+export interface ModalData {
+  image: ImageModel;
+}
+
+export interface Library {
+  add_pkgs: string[];
+  available_versions: string[];
+  error_message: string;
+  group: string;
+  name: string;
+  status: string;
+  version: string;
+}
+
+export interface ClusterConfig {
+  Classification: string;
+  Properties: Record<string, any>;
+  Configurations: any[];
+}
diff --git a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
index 885328fc2..931e037d6 100644
--- a/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
+++ b/services/self-service/src/main/resources/webapp/src/app/resources/resources.module.ts
@@ -36,7 +36,9 @@ import {CheckboxModule} from '../shared/checkbox';
 import {BubbleModule} from '../shared';
 import { CapitalizeFirstLetterPipeModule } from '../core/pipes';
 import { LocalDatePipeModule } from '../core/pipes/local-date-pipe';
-import { ShareImageModule } from '../shared/modal-dialog/share-image/share-image.module';
+import { ShareImageDialogModule } from './exploratory/share-image/share-image-dialog.module';
+import { ImageDetailDialogModule } from './exploratory/image-detail-dialog/image-detail-dialog.module';
+import {LibraryInfoModalModule} from './exploratory/library-info-modal/library-info-modal.module';
 
 @NgModule({
   imports: [
@@ -53,7 +55,9 @@ import { ShareImageModule } from '../shared/modal-dialog/share-image/share-image
     BubbleModule,
     CapitalizeFirstLetterPipeModule,
     LocalDatePipeModule,
-    ShareImageModule
+    ShareImageDialogModule,
+    ImageDetailDialogModule,
+    LibraryInfoModalModule,
   ],
   declarations: [
     ResourcesComponent,
diff --git a/services/self-service/src/main/resources/webapp/src/styles.scss b/services/self-service/src/main/resources/webapp/src/styles.scss
index 5bc005390..cd4e9d727 100644
--- a/services/self-service/src/main/resources/webapp/src/styles.scss
+++ b/services/self-service/src/main/resources/webapp/src/styles.scss
@@ -572,3 +572,12 @@ ace_scrollbar {
 .timezone-mat-select {
   max-width: 350px !important;
 }
+
+.library-dialog-container .mat-dialog-container {
+  width: 300px;
+  max-height: 210px;
+}
+
+.library-dialog-container ::-webkit-scrollbar {
+  width: 3px !important;
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datalab.apache.org
For additional commands, e-mail: commits-help@datalab.apache.org