From 8a0464f7d9cf1b667bb0dc096c7901f05c259e54 Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Mon, 21 Oct 2024 17:06:49 -0600
Subject: [PATCH 01/10] Added service to retrieve project and proposal info to
 UI

---
 .../app/workspaces/model/archive-models.ts    | 30 ++++++++++++++
 .../services/archive.service.spec.ts          | 35 ++++++++++++++++
 .../workspaces/services/archive.service.ts    | 40 +++++++++++++++++++
 3 files changed, 105 insertions(+)
 create mode 100644 apps/web/src/app/workspaces/model/archive-models.ts
 create mode 100644 apps/web/src/app/workspaces/services/archive.service.spec.ts
 create mode 100644 apps/web/src/app/workspaces/services/archive.service.ts

diff --git a/apps/web/src/app/workspaces/model/archive-models.ts b/apps/web/src/app/workspaces/model/archive-models.ts
new file mode 100644
index 000000000..94ec25da8
--- /dev/null
+++ b/apps/web/src/app/workspaces/model/archive-models.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2023 Associated Universities, Inc. Washington DC, USA.
+ *
+ * This file is part of NRAO Workspaces.
+ *
+ * Workspaces is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Workspaces is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+export interface ProductMetadata {
+  telescope: string;
+  project_code: string;
+  type: string;
+  date: Date;
+  mous_id: string | null;
+  segment: string | null;
+  title: string;
+  abstract: string;
+  observing_types: Array<string>;
+}
diff --git a/apps/web/src/app/workspaces/services/archive.service.spec.ts b/apps/web/src/app/workspaces/services/archive.service.spec.ts
new file mode 100644
index 000000000..cc1f8282a
--- /dev/null
+++ b/apps/web/src/app/workspaces/services/archive.service.spec.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 Associated Universities, Inc. Washington DC, USA.
+ *
+ * This file is part of NRAO Workspaces.
+ *
+ * Workspaces is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Workspaces is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+import { TestBed } from '@angular/core/testing';
+
+import { ArchiveService } from './archive.service';
+
+describe('ArchiveService', () => {
+  let service: ArchiveService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(ArchiveService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/apps/web/src/app/workspaces/services/archive.service.ts b/apps/web/src/app/workspaces/services/archive.service.ts
new file mode 100644
index 000000000..03ffc572e
--- /dev/null
+++ b/apps/web/src/app/workspaces/services/archive.service.ts
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 Associated Universities, Inc. Washington DC, USA.
+ *
+ * This file is part of NRAO Workspaces.
+ *
+ * Workspaces is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Workspaces is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { environment } from 'src/environments/environment';
+import { UpdateOutputFileStampsProject } from 'typescript';
+import { ProductMetadata } from '../model/archive-models';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ArchiveService {
+
+  constructor(private httpClient: HttpClient) { }
+
+  getMetadataForProduct(product_locator: string, include_proposal_info = true): Observable<ProductMetadata> {
+    let params = new HttpParams();
+    params = params.append("locator", product_locator);
+    params = params.append("include_proposal_info", include_proposal_info)
+    return this.httpClient.get<ProductMetadata>(`${environment.apiUrl}product/metadata`, {params: params});
+  }
+}
-- 
GitLab


From ae4a6c0fefffc9b98099501a654d47aace16734c Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 22 Oct 2024 12:36:12 -0600
Subject: [PATCH 02/10] Hooked up product-metadata endpoint to WS UI

---
 .../capability-request.component.html            |  3 +++
 .../capability-request.component.ts              | 16 ++++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
index 1781d9a84..0419b44e0 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
@@ -45,6 +45,9 @@
         (versionEvent)="setVersion($event)"
       ></app-versions>
       <br />
+      <span>{{productMetadata | json}}</span>
+      <br />
+      <br />
       <span id="parameters-label" *ngIf="currentVersion"
       >Version {{ currentVersion.version_number }} Parameters</span
       >
diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
index eb1a1ae39..6dee9e81f 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
@@ -32,6 +32,8 @@ import { JsonObject } from "@angular/compiler-cli/ngcc/src/packages/entry_point"
 import { Subject, Observable } from "rxjs";
 import { takeUntil, repeatWhen } from "rxjs/operators";
 import { Capability } from "../../model/capability";
+import { ProductMetadata } from "../../model/archive-models";
+import { ArchiveService } from "../../services/archive.service";
 
 @Component({
   selector: "app-capability-request",
@@ -49,6 +51,7 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
     private alertService: AlertService,
     private titleService: Title,
     private urlService: UrlService,
+    private archiveService: ArchiveService,
   ) {
     const requestID = parseInt(this.route.snapshot.paramMap.get("id"));
     this.pollingDataUpdaterService
@@ -73,6 +76,7 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
   public currentVersion: CapabilityVersion;
   public latestVersion: CapabilityVersion;
   public previousUrlString: string;
+  public productMetadata: ProductMetadata | null = null;
   activeRequestsPageDefaultUrl = "/workspaces/active-requests";
   previousUrl: Observable<string> = this.urlService.previousUrl$;
 
@@ -140,6 +144,9 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
           this.currentVersion.version_number === this.latestVersion.version_number
         ) {
           this.setVersion(this.latestVersion);
+          // Refresh product metadata whenever select version changes
+          //   since it owns the request's product locator
+          this.archiveService.getMetadataForProduct(this.latestVersion.parameters["product_locator"] as string).subscribe(this.productMetadataObserver);
         }
       } else {
         console.error("Current version returned undefined.");
@@ -148,6 +155,15 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
     error: (error) => console.error("Error when retrieving current version:" + error),
   };
 
+  private productMetadataObserver = {
+      next: (productMetadata: ProductMetadata) => {
+        if (productMetadata) {
+          this.productMetadata = productMetadata;
+        }
+      },
+      error: (error) => console.error(`Error when retrieving product metadata for CapabilityRequest ${this.capabilityRequest?.id} Version ${this.currentVersion.version_number} :` + error),
+    };
+
   public requestMessageObserver = {
     next: () => {
       window.location.reload();
-- 
GitLab


From e587f865f7286c0ce9760c1c1ed800ff7286011c Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Mon, 28 Oct 2024 12:22:20 -0600
Subject: [PATCH 03/10] Added ngb-modal-draggable library's source code from GH

---
 .licenserc.yaml                               |  2 +
 .../app/shared/ngb-modal-draggable/LICENSE    | 21 ++++++
 .../app/shared/ngb-modal-draggable/README.md  | 50 ++++++++++++++
 .../ngb-modal-draggable.directive.ts          | 66 +++++++++++++++++++
 .../ngb-modal-draggable.module.ts             | 13 ++++
 5 files changed, 152 insertions(+)
 create mode 100644 apps/web/src/app/shared/ngb-modal-draggable/LICENSE
 create mode 100644 apps/web/src/app/shared/ngb-modal-draggable/README.md
 create mode 100644 apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
 create mode 100644 apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.module.ts

diff --git a/.licenserc.yaml b/.licenserc.yaml
index 9f841f4ef..85be0da9c 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -28,5 +28,7 @@ header:
     - '**/node_modules/**'
     - '**/cli/executables/pexable/pycapo/**'
     - '**/schema/versions/**'
+    # This is third-party code provided under the MIT license
+    - 'apps/web/src/app/shared/ngb-modal-draggable'
 
   license-location-threshold: 20
diff --git a/apps/web/src/app/shared/ngb-modal-draggable/LICENSE b/apps/web/src/app/shared/ngb-modal-draggable/LICENSE
new file mode 100644
index 000000000..caf0a3f00
--- /dev/null
+++ b/apps/web/src/app/shared/ngb-modal-draggable/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Vanden Bosch Cédric
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/apps/web/src/app/shared/ngb-modal-draggable/README.md b/apps/web/src/app/shared/ngb-modal-draggable/README.md
new file mode 100644
index 000000000..4f96069a1
--- /dev/null
+++ b/apps/web/src/app/shared/ngb-modal-draggable/README.md
@@ -0,0 +1,50 @@
+**Copied from [https://github.com/mattxu-zz/ngb-modal-draggable](https://github.com/mattxu-zz/ngb-modal-draggable)**
+
+### NgbModalDraggable
+
+Angular Directive used for make ngbModal draggable
+
+### Install
+
+```
+npm i ngb-modal-draggable --save
+```
+
+### How to use
+
+First import NgbModalDraggableModule in your modules
+
+```
+import { NgModule } from '@angular/core';
+import { NgbModalDraggableModule } from 'ngb-modal-draggable'
+
+@NgModule({
+    imports: [
+        ....
+
+        NgbModalDraggableModule
+    ],
+    ...
+})
+```
+
+Then just use in your modal component html
+
+```
+<div ngb-modal-draggable [ngbModalDraggableHandle]="draggableHandle">
+  <div #draggableHandle class="modal-header">
+    <h4 class="modal-title" id="modal-basic-title">Profile update</h4>
+    <button type="button" class="close" aria-label="Close" (click)="modal.dismiss('Cross click')">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  ...
+</div>
+```
+
+### Properties
+
+| Input | Type | Default | Description |
+| ------ | ------ | ------ | ------ |
+| ngbModalDraggableHandle | HTMLElement | null | Use template variable to refer to the handle element. Then only the handle element is draggable |
+| ngbModalRootLevel | number | 2 | Set the root level from current element which set the directive. If you set the directive on the root element of the NgbModal component, the true root level would be 2 as the NgbModalService would add two parent element automatically |
diff --git a/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
new file mode 100644
index 000000000..3e0c0d2fb
--- /dev/null
+++ b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
@@ -0,0 +1,66 @@
+import { Directive, ElementRef, HostListener, AfterViewInit, Input } from "@angular/core";
+@Directive({
+    selector: '[ngb-modal-draggable]'
+})
+export class NgbModalDraggable implements AfterViewInit {
+    private modalElement: HTMLElement;
+    private topStart: number;
+    private leftStart: number;
+    private md: boolean;
+    private handleElement: HTMLElement;
+    private rootLevel: number = 2;
+
+    constructor(public element: ElementRef) {
+    }
+
+    ngAfterViewInit() {
+        let element = this.element.nativeElement;
+        for (let level = this.rootLevel; level > 0; level --) {
+            element = element.parentNode;
+        }
+
+        this.modalElement = element;
+
+        this.modalElement.style.position = 'relative';
+        this.modalElement.className += ' cursor-draggable';
+    }
+
+    @HostListener('mousedown', ['$event'])
+    onMouseDown(event: MouseEvent) {
+        if (event.button === 2 || (this.handleElement && event.target !== this.handleElement))
+            return; // prevents right click drag, remove this if you don't want it
+        this.md = true;
+        this.topStart = event.clientY - Number(this.modalElement.style.top.replace('px', ''));
+        this.leftStart = event.clientX - Number(this.modalElement.style.left.replace('px', ''));
+        event.preventDefault();
+    }
+
+    @HostListener('document:mouseup', ['$event'])
+    onMouseUp(event: MouseEvent) {
+        this.md = false;
+    }
+
+    @HostListener('document:mousemove', ['$event'])
+    onMouseMove(event: MouseEvent) {
+        //console.dir(event.target)
+        if (this.md) {
+            this.modalElement.style.top = (event.clientY - this.topStart) + 'px';
+            this.modalElement.style.left = (event.clientX - this.leftStart) + 'px';
+        }
+    }
+
+    @HostListener('document:mouseleave', ['$event'])
+    onMouseLeave(event: MouseEvent) {
+        this.md = false;
+    }
+
+    @Input()
+    set ngbModalDraggableHandle(handle: HTMLElement){
+        this.handleElement = handle;
+    }
+
+    @Input()
+    set ngbModalRootLevel(level: number) {
+        this.rootLevel = level;
+    }
+}
diff --git a/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.module.ts b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.module.ts
new file mode 100644
index 000000000..c0299f9f8
--- /dev/null
+++ b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.module.ts
@@ -0,0 +1,13 @@
+import {NgModule} from '@angular/core';
+import {NgbModalDraggable} from './ngb-modal-draggable.directive';
+
+@NgModule({
+  declarations:[
+    NgbModalDraggable
+  ],
+  exports:[
+    NgbModalDraggable
+  ]
+})
+
+export class NgbModalDraggableModule{}
-- 
GitLab


From fb69f2ccaa8e54013bdb168c8f6b86e13411933a Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 29 Oct 2024 13:05:08 -0600
Subject: [PATCH 04/10] Updated cursor when dragging & fixed bugs in
 ngb-modal-draggable

---
 .../ngb-modal-draggable.directive.ts          | 31 +++++++++++++++----
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
index 3e0c0d2fb..847c39035 100644
--- a/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
+++ b/apps/web/src/app/shared/ngb-modal-draggable/ngb-modal-draggable.directive.ts
@@ -23,20 +23,38 @@ export class NgbModalDraggable implements AfterViewInit {
 
         this.modalElement.style.position = 'relative';
         this.modalElement.className += ' cursor-draggable';
+        this.element = new ElementRef(this.modalElement);
+
+        if (!this.handleElement) {
+            this.handleElement = this.modalElement;
+        }
+
+        this.handleElement.addEventListener('mouseenter', (event: MouseEvent) => {this.modalElement.style.cursor = "grab";})
+        this.handleElement.addEventListener('mouseleave', (event: MouseEvent) => {
+            if (!this.md) {
+                this.modalElement.style.cursor = "auto";
+            }
+        })
     }
 
     @HostListener('mousedown', ['$event'])
     onMouseDown(event: MouseEvent) {
-        if (event.button === 2 || (this.handleElement && event.target !== this.handleElement))
-            return; // prevents right click drag, remove this if you don't want it
-        this.md = true;
-        this.topStart = event.clientY - Number(this.modalElement.style.top.replace('px', ''));
-        this.leftStart = event.clientX - Number(this.modalElement.style.left.replace('px', ''));
-        event.preventDefault();
+        if (event.button !== 2 && (event.target instanceof Element && this.handleElement.contains(event.target))) {
+            this.md = true;
+            this.modalElement.style.cursor = "grabbing";
+            this.topStart = event.clientY - Number(this.modalElement.style.top.replace('px', ''));
+            this.leftStart = event.clientX - Number(this.modalElement.style.left.replace('px', ''));
+            event.preventDefault();
+        }
     }
 
     @HostListener('document:mouseup', ['$event'])
     onMouseUp(event: MouseEvent) {
+        if (event.target instanceof Element && this.handleElement.contains(event.target)) {
+            this.modalElement.style.cursor = "grab";
+        } else {
+            this.modalElement.style.cursor = "auto";
+        }
         this.md = false;
     }
 
@@ -52,6 +70,7 @@ export class NgbModalDraggable implements AfterViewInit {
     @HostListener('document:mouseleave', ['$event'])
     onMouseLeave(event: MouseEvent) {
         this.md = false;
+        this.modalElement.style.cursor = "auto";
     }
 
     @Input()
-- 
GitLab


From 03e178e747cb2e6f2a4274435d0460c34e60b8d6 Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 29 Oct 2024 11:31:38 -0600
Subject: [PATCH 05/10] Added modal displaying prop-info to request page

---
 .../capability-request.component.html         | 12 +++--
 .../proposal-info.component.html              | 42 +++++++++++++++
 .../proposal-info.component.scss              |  0
 .../proposal-info.component.spec.ts           | 43 +++++++++++++++
 .../proposal-info/proposal-info.component.ts  | 54 +++++++++++++++++++
 .../src/app/workspaces/workspaces.module.ts   |  4 ++
 6 files changed, 152 insertions(+), 3 deletions(-)
 create mode 100644 apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
 create mode 100644 apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
 create mode 100644 apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.spec.ts
 create mode 100644 apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts

diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
index 0419b44e0..7e4cabf77 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
@@ -45,9 +45,6 @@
         (versionEvent)="setVersion($event)"
       ></app-versions>
       <br />
-      <span>{{productMetadata | json}}</span>
-      <br />
-      <br />
       <span id="parameters-label" *ngIf="currentVersion"
       >Version {{ currentVersion.version_number }} Parameters</span
       >
@@ -117,6 +114,15 @@
         [currentVersion]="this.currentVersion"
       ></internal-notes>
       <br />
+      <span id="metadata-label" *ngIf="productMetadata"
+      >Version {{ currentVersion.version_number }} Proposal Information</span
+      >
+      <app-proposal-info
+        *ngIf="productMetadata"
+        id="internal-notes-button"
+        [productMetadata]="productMetadata"
+      ></app-proposal-info>
+      <br />
       <app-request-operations
         id="operations"
         class="pt-2"
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
new file mode 100644
index 000000000..9c70b651a
--- /dev/null
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
@@ -0,0 +1,42 @@
+<div id="proposal-info-container"
+class="container-fluid rounded-top rounded-3 p-3 bg-light"
+*ngIf="title && abstract && project_code"
+>
+  <div class="row my-2">
+    <div class="col">
+      <div class="d-flex justify-content-left">
+        <button
+          id="open-editor-modal-button"
+          type="button"
+          class="btn btn-primary"
+          (click)="open(editor)"
+        >
+        Version N Proposal Information
+        </button>
+      </div>
+    </div>
+  </div>
+</div>
+<ng-template #editor let-modal>
+    <div ngb-modal-draggable [ngbModalDraggableHandle]="editormodalheader">
+        <div #editormodalheader class="modal-header">
+            <h5 class="modal-title" id="modal-label">Proposal Information</h5>
+            <button type="button" class="close" aria-label="Close" (click)="modal.close('exit')">
+                <span aria-hidden="true">&times;</span>
+            </button>
+        </div>
+        <div class="modal-body" style="line-height:1.5">
+            <span class="font-weight-bold">Archive Project Code:</span> {{project_code}}
+            <br/>
+            <span class="font-weight-bold">Title:</span> {{title}}
+            <br/>
+            <span class="font-weight-bold">Observing Types</span>
+            <ul>
+                <li *ngFor="let observing_type of observing_types">{{observing_type}}</li>
+            </ul>
+            <span class="font-weight-bold">Abstract</span>
+            <br/>
+            <span style="line-height:2">{{abstract}}</span>
+        </div>
+    </div>
+</ng-template>
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.spec.ts b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.spec.ts
new file mode 100644
index 000000000..c982b27f0
--- /dev/null
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.spec.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 Associated Universities, Inc. Washington DC, USA.
+ *
+ * This file is part of NRAO Workspaces.
+ *
+ * Workspaces is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Workspaces is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
+ */
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProposalInfoComponent } from './proposal-info.component';
+
+describe('ProposalInfoComponent', () => {
+  let component: ProposalInfoComponent;
+  let fixture: ComponentFixture<ProposalInfoComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ProposalInfoComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ProposalInfoComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
new file mode 100644
index 000000000..64005eb69
--- /dev/null
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 Associated Universities, Inc. Washington DC, USA.
+ *
+ * This file is part of NRAO Workspaces.
+ *
+ * Workspaces is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Workspaces is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
+ */
+import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
+import { ProductMetadata } from 'src/app/workspaces/model/archive-models';
+
+@Component({
+  selector: 'app-proposal-info',
+  templateUrl: './proposal-info.component.html',
+  styleUrls: ['./proposal-info.component.scss']
+})
+export class ProposalInfoComponent implements OnInit {
+  @Output() editorOpenEvent = new EventEmitter<boolean>();
+  @Input() set productMetadata(productMetadata: ProductMetadata) {
+    if (productMetadata) {
+      this.title = productMetadata.title;
+      this.abstract = productMetadata.abstract;
+      this.observing_types = productMetadata.observing_types;
+      this.project_code = productMetadata.project_code;
+    }
+  }
+
+  public title: string;
+  public abstract: string;
+  public observing_types: Array<string>;
+  public project_code: string;
+
+  constructor(
+    private modalService: NgbModal,
+  ) { }
+
+  public open(content) {
+    this.modalService.open(content, { ariaLabelledBy: "modal-title", centered: true, scrollable: true });
+  }
+
+  ngOnInit(): void {}
+
+}
diff --git a/apps/web/src/app/workspaces/workspaces.module.ts b/apps/web/src/app/workspaces/workspaces.module.ts
index 701475af5..a0a8a4b80 100644
--- a/apps/web/src/app/workspaces/workspaces.module.ts
+++ b/apps/web/src/app/workspaces/workspaces.module.ts
@@ -41,6 +41,7 @@ import {QaControlsComponent} from './components/capability-request/components/qa
 import {SendEmailComponent} from './components/send-email/send-email.component';
 import {CapabilityDataAccessComponent} from "./components/capability-request/components/capability-data-access/capability-data-access.component";
 import {InternalNotesComponent} from './components/capability-request/components/./internal-notes/internal-notes.component';
+import { ProposalInfoComponent } from './components/capability-request/components/proposal-info/proposal-info.component'
 import {WsHeaderComponent} from './ws-header/ws-header.component';
 import {WsHomeComponent} from './ws-home/ws-home.component';
 import {MatAutocompleteModule} from '@angular/material/autocomplete';
@@ -48,11 +49,13 @@ import {MatFormFieldModule} from '@angular/material/form-field';
 import {MatSelectModule} from '@angular/material/select';
 import {MatInputModule} from '@angular/material/input';
 import {MatPaginatorModule} from '@angular/material/paginator';
+import { NgbModalDraggableModule } from "../shared/ngb-modal-draggable/ngb-modal-draggable.module";
 
 @NgModule({
   declarations: [
     WorkspacesComponent,
     CapabilityRequestComponent,
+    ProposalInfoComponent,
     RequestHeaderComponent,
     StatusBadgeComponent,
     ParametersComponent,
@@ -77,6 +80,7 @@ import {MatPaginatorModule} from '@angular/material/paginator';
   imports: [
     CommonModule,
     NgbModule,
+    NgbModalDraggableModule,
     WorkspacesRoutingModule,
     ReactiveFormsModule,
     FormsModule,
-- 
GitLab


From 7df611fc663965849f0eb131779d29eaffc26181 Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 29 Oct 2024 14:53:13 -0600
Subject: [PATCH 06/10] Got prop-info modal to scroll

---
 .../components/proposal-info/proposal-info.component.html | 2 +-
 .../components/proposal-info/proposal-info.component.scss | 8 ++++++++
 .../components/proposal-info/proposal-info.component.ts   | 3 +--
 3 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
index 9c70b651a..985642455 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
@@ -18,7 +18,7 @@ class="container-fluid rounded-top rounded-3 p-3 bg-light"
   </div>
 </div>
 <ng-template #editor let-modal>
-    <div ngb-modal-draggable [ngbModalDraggableHandle]="editormodalheader">
+    <div ngb-modal-draggable [ngbModalDraggableHandle]="editormodalheader" class="fix-ngb-modal-scrollable">
         <div #editormodalheader class="modal-header">
             <h5 class="modal-title" id="modal-label">Proposal Information</h5>
             <button type="button" class="close" aria-label="Close" (click)="modal.close('exit')">
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
index e69de29bb..0bdce7460 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
@@ -0,0 +1,8 @@
+/** The style below allows the `scrollable` property on the NgbModal to work with the ngb-modal-draggable directive
+    Source: https://github.com/ng-bootstrap/ng-bootstrap/issues/3281#issuecomment-509701024
+**/
+.fix-ngb-modal-scrollable {
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
index 64005eb69..20cc95911 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
@@ -26,7 +26,6 @@ import { ProductMetadata } from 'src/app/workspaces/model/archive-models';
   styleUrls: ['./proposal-info.component.scss']
 })
 export class ProposalInfoComponent implements OnInit {
-  @Output() editorOpenEvent = new EventEmitter<boolean>();
   @Input() set productMetadata(productMetadata: ProductMetadata) {
     if (productMetadata) {
       this.title = productMetadata.title;
@@ -46,7 +45,7 @@ export class ProposalInfoComponent implements OnInit {
   ) { }
 
   public open(content) {
-    this.modalService.open(content, { ariaLabelledBy: "modal-title", centered: true, scrollable: true });
+    this.modalService.open(content, { ariaLabelledBy: "modal-title", scrollable: true });
   }
 
   ngOnInit(): void {}
-- 
GitLab


From 823c22c60abad424928a1f7a2e99f78153b9b0de Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 29 Oct 2024 15:03:59 -0600
Subject: [PATCH 07/10] Got body to scroll in the background while modal is
 open

---
 .../components/proposal-info/proposal-info.component.ts        | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
index 20cc95911..8181d5ec9 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
@@ -46,6 +46,9 @@ export class ProposalInfoComponent implements OnInit {
 
   public open(content) {
     this.modalService.open(content, { ariaLabelledBy: "modal-title", scrollable: true });
+    // NgbModal prohibits the body from scrolling by default.
+    // Prevent that here so that DAs can see all the other data on the page while the modal is open
+    document.body.style.overflow = "inherit";
   }
 
   ngOnInit(): void {}
-- 
GitLab


From 464adf98e8f028fd572d0f9e858d8b3884df2bef Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Tue, 29 Oct 2024 17:40:02 -0600
Subject: [PATCH 08/10] Polished up CSS for prop-info modal

---
 .../proposal-info.component.html              | 31 ++++++++++++-------
 .../proposal-info.component.scss              | 12 +++++++
 2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
index 985642455..426ec8a4a 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
@@ -25,18 +25,25 @@ class="container-fluid rounded-top rounded-3 p-3 bg-light"
                 <span aria-hidden="true">&times;</span>
             </button>
         </div>
-        <div class="modal-body" style="line-height:1.5">
-            <span class="font-weight-bold">Archive Project Code:</span> {{project_code}}
-            <br/>
-            <span class="font-weight-bold">Title:</span> {{title}}
-            <br/>
-            <span class="font-weight-bold">Observing Types</span>
-            <ul>
-                <li *ngFor="let observing_type of observing_types">{{observing_type}}</li>
-            </ul>
-            <span class="font-weight-bold">Abstract</span>
-            <br/>
-            <span style="line-height:2">{{abstract}}</span>
+        <div class="modal-body">
+            <div class="row flex-column">
+              <span class="col font-weight-bold">Archive Project Code</span>
+              <span class="col">{{project_code}}</span>
+            </div>
+            <div class="row flex-column">
+              <span class="col font-weight-bold">Title</span>
+              <span class="col">{{title}}</span>
+            </div>
+            <div class="row flex-column">
+              <span class="col font-weight-bold">Observing Types</span>
+              <ul class="col">
+                  <li *ngFor="let observing_type of observing_types">{{observing_type}}</li>
+              </ul>
+            </div>
+            <div class="row flex-column">
+              <span class="col font-weight-bold">Abstract</span>
+              <span class="col" id="proposal-info-abstract">{{abstract}}</span>
+            </div>
         </div>
     </div>
 </ng-template>
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
index 0bdce7460..cba4aebd3 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
@@ -6,3 +6,15 @@
   flex-direction: column;
   overflow: hidden;
 }
+
+.modal-body .row {
+  margin-bottom: 0.5em;
+}
+
+.modal-body ul {
+  padding-left: 3em;
+}
+
+#proposal-info-abstract {
+  line-height: 2;
+}
-- 
GitLab


From 3a7aeb52455e930efbc7040009a8703cc96cf493 Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Wed, 30 Oct 2024 09:30:18 -0600
Subject: [PATCH 09/10] Fixed bug when currVer changes, tweaked other stuff

---
 .../capability-request.component.html                  | 10 ++++++----
 .../capability-request/capability-request.component.ts |  8 ++++----
 .../proposal-info/proposal-info.component.html         |  2 +-
 .../proposal-info/proposal-info.component.scss         |  2 +-
 4 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
index 7e4cabf77..5ba199972 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
@@ -114,11 +114,13 @@
         [currentVersion]="this.currentVersion"
       ></internal-notes>
       <br />
-      <span id="metadata-label" *ngIf="productMetadata"
-      >Version {{ currentVersion.version_number }} Proposal Information</span
-      >
+      <!--Because productMetadata is populated on setVersion(), currentVersion should always exist if pM exists.
+          The checks below are just added security.
+      -->
+      <span id="metadata-label" *ngIf="productMetadata && currentVersion"
+      >Version {{ currentVersion.version_number }} Proposal Information</span>
       <app-proposal-info
-        *ngIf="productMetadata"
+        *ngIf="productMetadata && currentVersion"
         id="internal-notes-button"
         [productMetadata]="productMetadata"
       ></app-proposal-info>
diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
index 6dee9e81f..daa2c6200 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
@@ -94,7 +94,7 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
         ];
       }
       if (!this.currentVersion) {
-        this.currentVersion = this.latestVersion;
+        this.setVersion(this.latestVersion);
       }
       if (this.currentVersion.current_execution) {
         this.capabilityExecution = this.currentVersion.current_execution;
@@ -144,9 +144,6 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
           this.currentVersion.version_number === this.latestVersion.version_number
         ) {
           this.setVersion(this.latestVersion);
-          // Refresh product metadata whenever select version changes
-          //   since it owns the request's product locator
-          this.archiveService.getMetadataForProduct(this.latestVersion.parameters["product_locator"] as string).subscribe(this.productMetadataObserver);
         }
       } else {
         console.error("Current version returned undefined.");
@@ -198,6 +195,9 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
 
   setVersion(version: CapabilityVersion): void {
     this.currentVersion = version;
+    // Refresh product metadata whenever select version changes
+    //   since it owns the request's product locator
+    this.archiveService.getMetadataForProduct(this.currentVersion.parameters["product_locator"] as string).subscribe(this.productMetadataObserver);
   }
 
   catchOpenEditorEventFromChild(isEditorOpen: boolean): void {
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
index 426ec8a4a..d1b5ec86c 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.html
@@ -11,7 +11,7 @@ class="container-fluid rounded-top rounded-3 p-3 bg-light"
           class="btn btn-primary"
           (click)="open(editor)"
         >
-        Version N Proposal Information
+        Proposal Information
         </button>
       </div>
     </div>
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
index cba4aebd3..d69489202 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.scss
@@ -4,7 +4,7 @@
 .fix-ngb-modal-scrollable {
   display: flex;
   flex-direction: column;
-  overflow: hidden;
+  overflow-y: hidden;
 }
 
 .modal-body .row {
-- 
GitLab


From fb89a2512b1335fa5a251cadbef08becb73012d2 Mon Sep 17 00:00:00 2001
From: Sam Kagan <skagan@nrao.edu>
Date: Thu, 31 Oct 2024 13:50:57 -0600
Subject: [PATCH 10/10] Added cleanup for product-metadata Observable, removed
 unused imports

Also fixed a bug where title for Prop Info would show up even when button wouldn't
---
 .../capability-request.component.html                 |  4 ++--
 .../capability-request.component.ts                   | 11 ++++++++++-
 .../proposal-info/proposal-info.component.ts          |  2 +-
 .../src/app/workspaces/services/archive.service.ts    |  1 -
 4 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
index 5ba199972..cd87651b1 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.html
@@ -117,10 +117,10 @@
       <!--Because productMetadata is populated on setVersion(), currentVersion should always exist if pM exists.
           The checks below are just added security.
       -->
-      <span id="metadata-label" *ngIf="productMetadata && currentVersion"
+      <span id="metadata-label" *ngIf="canDisplayProposalInformation()"
       >Version {{ currentVersion.version_number }} Proposal Information</span>
       <app-proposal-info
-        *ngIf="productMetadata && currentVersion"
+        *ngIf="canDisplayProposalInformation()"
         id="internal-notes-button"
         [productMetadata]="productMetadata"
       ></app-proposal-info>
diff --git a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
index daa2c6200..1dd84ee21 100644
--- a/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/capability-request.component.ts
@@ -197,7 +197,12 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
     this.currentVersion = version;
     // Refresh product metadata whenever select version changes
     //   since it owns the request's product locator
-    this.archiveService.getMetadataForProduct(this.currentVersion.parameters["product_locator"] as string).subscribe(this.productMetadataObserver);
+    this.archiveService.getMetadataForProduct(this.currentVersion.parameters["product_locator"] as string)
+      .pipe(
+        takeUntil(this.ngUnsubscribe),
+        repeatWhen(() => this.ngUnsubscribe)
+      )
+    .subscribe(this.productMetadataObserver);
   }
 
   catchOpenEditorEventFromChild(isEditorOpen: boolean): void {
@@ -232,6 +237,10 @@ export class CapabilityRequestComponent implements OnInit, OnDestroy {
       .subscribe(this.requestMessageObserver);
   }
 
+  public canDisplayProposalInformation() {
+    return this.productMetadata && this.productMetadata.title && this.productMetadata.abstract && this.productMetadata.project_code && this.currentVersion;
+  }
+
   ngOnInit(): void {
     this.urlService.previousUrl$.subscribe((previousUrl: string) => {
       this.previousUrlString = previousUrl;
diff --git a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
index 8181d5ec9..313bcdbbe 100644
--- a/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
+++ b/apps/web/src/app/workspaces/components/capability-request/components/proposal-info/proposal-info.component.ts
@@ -16,7 +16,7 @@
  * You should have received a copy of the GNU General Public License
  * along with Workspaces.  If not, see <https://www.gnu.org/licenses/>.
  */
-import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
+import { Component, OnInit, Input } from '@angular/core';
 import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
 import { ProductMetadata } from 'src/app/workspaces/model/archive-models';
 
diff --git a/apps/web/src/app/workspaces/services/archive.service.ts b/apps/web/src/app/workspaces/services/archive.service.ts
index 03ffc572e..f98c0de09 100644
--- a/apps/web/src/app/workspaces/services/archive.service.ts
+++ b/apps/web/src/app/workspaces/services/archive.service.ts
@@ -21,7 +21,6 @@ import { HttpClient, HttpParams } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 import { environment } from 'src/environments/environment';
-import { UpdateOutputFileStampsProject } from 'typescript';
 import { ProductMetadata } from '../model/archive-models';
 
 @Injectable({
-- 
GitLab