+ No drafts were found for these conditions under local storage. Substance
+ drafts are stored in this browser's cache, so using an incognito tab or
+ clearing cache will clear or not allow access to drafts. You can save and
+ load stored record drafts using the buttons below.
+
- No drafts were found for these conditions under local storage. Substance
- drafts are stored in this browser's cache, so using an incognito tab or
- clearing cache will clear or not allow access to drafts. You can save and
- load stored record drafts using the buttons below.
-
-
-
-
-
-
- Show only current record
-
+ Show only new registrations
+
+
diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss
index 4b0b78ac2..17fc00d47 100644
--- a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss
+++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.scss
@@ -157,6 +157,16 @@ hr.style {
*/
}
+.related-substance-image {
+ /* make the interpreted draft image responsive to available space */
+ width: 100%;
+ img {
+ width: 100%;
+ height: auto;
+ max-width: 100%;
+ }
+}
+
/*
.related-substance {
min-width: 387px;
diff --git a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts
index eef48f8ee..464fc350b 100644
--- a/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts
+++ b/src/app/core/substance-ssg4m/ssg4m-starting-materials/ssg4m-starting-materials-form.component.ts
@@ -178,11 +178,38 @@ export class Ssg4mStartingMaterialsFormComponent implements OnInit, OnDestroy {
approvalID: substance.approvalID
};
this.privateStartingMaterial.substanceName = relatedSubstance;
+ // Clear any draft-related fields when selecting from database
+ delete (this.privateStartingMaterial as any).$$tmpStructureId;
} else {
this.privateStartingMaterial.substanceName = {};
}
}
+ draftSubstanceSelected(event: any): void {
+ if (!event) {
+ return;
+ }
+
+ const { substance: substanceObj, primaryName, tmpStructureId } = event;
+
+ console.log('Draft substance selected: ', substanceObj, primaryName, tmpStructureId);
+
+ // Populate the starting material with draft data
+ this.privateStartingMaterial.substanceName = {
+ refPname: primaryName,
+ name: primaryName,
+ refuuid: substanceObj?.uuid || "draft",
+ substanceClass: "mention",
+ approvalID: substanceObj?.approvalID
+ };
+ this.privateStartingMaterial.verbatimName = primaryName;
+
+ // Store the temporary structure ID for rendering
+ if (tmpStructureId) {
+ (this.privateStartingMaterial as any).$$tmpStructureId = tmpStructureId;
+ }
+ }
+
confirmDeleteStartingMaterial() {
const dialogRef = this.dialog.open(ConfirmDialogComponent, {
data: { message: 'Are you sure you want to delele Input Material ' + (this.startingMaterialIndex + 1) + ' for Step ' + (this.stageIndex + 1) + ' for Site ' + (this.siteIndex + 1) + ' for Process ' + (this.processIndex + 1) + '?' }
diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html
index ebcf455b8..29f5c7c22 100644
--- a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html
+++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.html
@@ -25,6 +25,9 @@
Save Local Copy
+
+ Save Local Batch
+ Import Local CopyShow Data Structure
@@ -36,7 +39,7 @@
Save
+ [disabled]="showFormReadOnly === 'true' || isAuthenticated === false">Save
diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts
index a05aa8d4a..1033c0c47 100644
--- a/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts
+++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.component.ts
@@ -5,60 +5,77 @@ import {
ViewChildren,
ViewContainerRef,
QueryList,
- OnDestroy, HostListener
-} from '@angular/core';
-import { ActivatedRoute, Router, Event, NavigationStart, NavigationEnd } from '@angular/router';
-import { OverlayContainer } from '@angular/cdk/overlay';
-import { MatExpansionPanel } from '@angular/material/expansion';
-import { MatDialog } from '@angular/material/dialog';
-import { take, map } from 'rxjs/operators';
-import { Subscription, Observable } from 'rxjs';
-import * as _ from 'lodash';
-import * as moment from 'moment';
-import { Title } from '@angular/platform-browser';
-import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
+ OnDestroy,
+ HostListener,
+} from "@angular/core";
+import {
+ ActivatedRoute,
+ Router,
+ Event,
+ NavigationStart,
+ NavigationEnd,
+} from "@angular/router";
+import { OverlayContainer } from "@angular/cdk/overlay";
+import { MatExpansionPanel } from "@angular/material/expansion";
+import { MatDialog } from "@angular/material/dialog";
+import { take, map } from "rxjs/operators";
+import { Subscription, Observable } from "rxjs";
+import * as _ from "lodash";
+import * as moment from "moment";
+import { Title } from "@angular/platform-browser";
+import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
// GSRS Import
-import { ConfigService } from '@gsrs-core/config/config.service';
-import { GoogleAnalyticsService } from '../google-analytics/google-analytics.service';
-import { DynamicComponentLoader } from '../dynamic-component-loader/dynamic-component-loader.service';
-import { formSections } from '../substance-form/form-sections.constant';
-import { SubstanceFormSection } from '../substance-form/substance-form-section';
-import { MainNotificationService } from '../main-notification/main-notification.service';
-import { AuthService } from '@gsrs-core/auth';
-import { LoadingService } from '../loading/loading.service';
-import { SubstanceService } from '../substance/substance.service';
-import { SubstanceFormService } from '../substance-form/substance-form.service';
-import { AppNotification, NotificationType } from '../main-notification/notification.model';
-import { ValidationResults } from '../substance-form/substance-form.model';
-import { SubstanceDetail } from '../substance/substance.model';
-import { ValidationMessage, SubstanceFormResults, SubstanceFormDefinition } from '../substance-form/substance-form.model';
-import { SubmitSuccessDialogComponent } from '../substance-form/submit-success-dialog/submit-success-dialog.component';
-import { MergeConceptDialogComponent } from '@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component';
-import { DefinitionSwitchDialogComponent } from '@gsrs-core/substance-form/definition-switch-dialog/definition-switch-dialog.component';
-import { SubstanceEditImportDialogComponent } from '@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component';
-import { JsonDialogComponent } from '@gsrs-core/substance-form/json-dialog/json-dialog.component';
-import { SubstanceSsg4mService } from './substance-ssg4m-form.service';
-import { environment } from '@gsrs-core/../../environments/environment';
-import { Ssg4mSyntheticPathway } from './model/substance-ssg4m.model';
+import { ConfigService } from "@gsrs-core/config/config.service";
+import { GoogleAnalyticsService } from "../google-analytics/google-analytics.service";
+import { DynamicComponentLoader } from "../dynamic-component-loader/dynamic-component-loader.service";
+import { formSections } from "../substance-form/form-sections.constant";
+import { SubstanceFormSection } from "../substance-form/substance-form-section";
+import { MainNotificationService } from "../main-notification/main-notification.service";
+import { AuthService } from "@gsrs-core/auth";
+import { LoadingService } from "../loading/loading.service";
+import { SubstanceService } from "../substance/substance.service";
+import { SubstanceFormService } from "../substance-form/substance-form.service";
+import {
+ AppNotification,
+ NotificationType,
+} from "../main-notification/notification.model";
+import { ValidationResults } from "../substance-form/substance-form.model";
+import { SubstanceDetail } from "../substance/substance.model";
+import {
+ ValidationMessage,
+ SubstanceFormResults,
+ SubstanceFormDefinition,
+} from "../substance-form/substance-form.model";
+import { SubmitSuccessDialogComponent } from "../substance-form/submit-success-dialog/submit-success-dialog.component";
+import { MergeConceptDialogComponent } from "@gsrs-core/substance-form/merge-concept-dialog/merge-concept-dialog.component";
+import { DefinitionSwitchDialogComponent } from "@gsrs-core/substance-form/definition-switch-dialog/definition-switch-dialog.component";
+import { SubstanceEditImportDialogComponent } from "@gsrs-core/substance-edit-import-dialog/substance-edit-import-dialog.component";
+import { JsonDialogComponent } from "@gsrs-core/substance-form/json-dialog/json-dialog.component";
+import { SubstanceSsg4mService } from "./substance-ssg4m-form.service";
+import { environment } from "@gsrs-core/../../environments/environment";
+import { Ssg4mSyntheticPathway } from "./model/substance-ssg4m.model";
+import { toSvg } from "html-to-image";
import jp from 'jsonpath';
@Component({
- selector: 'app-substance-ssg4m-form',
- templateUrl: './substance-ssg4m-form.component.html',
- styleUrls: ['./substance-ssg4m-form.component.scss'],
- standalone: false
+ selector: "app-substance-ssg4m-form",
+ templateUrl: "./substance-ssg4m-form.component.html",
+ styleUrls: ["./substance-ssg4m-form.component.scss"],
+ standalone: false
})
-export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewInit, OnDestroy {
+export class SubstanceSsg4ManufactureFormComponent
+ implements OnInit, AfterViewInit, OnDestroy
+{
isLoading = true;
id?: string;
formSections: Array = [];
- @ViewChildren('dynamicComponent', { read: ViewContainerRef }) dynamicComponents: QueryList;
- @ViewChildren('expansionPanel', { read: MatExpansionPanel }) matExpansionPanels: QueryList;
+ @ViewChildren("dynamicComponent", { read: ViewContainerRef })
+ dynamicComponents: QueryList;
+ @ViewChildren("expansionPanel", { read: MatExpansionPanel })
+ matExpansionPanels: QueryList;
private subClass: string;
definitionType: string;
- expandedComponents = [
- 'substance-form-ssg4m-process'
- ];
+ expandedComponents = ["substance-form-ssg4m-process"];
showSubmissionMessages = false;
submissionMessage: string;
validationMessages: Array;
@@ -72,6 +89,9 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
definition: SubstanceFormDefinition;
user: string;
feature: string;
+ isAdmin: boolean;
+ isUpdater: boolean;
+ isAuthenticated: boolean;
messageField: string;
errorMessage: string;
microserviceStatusUp = false;
@@ -79,17 +99,18 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
substanceClass: string;
status: string;
classes = [
- 'concept',
- 'protein',
- 'chemical',
- 'structurallyDiverse',
- 'polymer',
- 'nucleicAcid',
- 'mixture',
- 'specifiedSubstanceG1',
- 'specifiedSubstanceG2',
- 'specifiedSubstanceG3',
- 'specifiedSubstanceG4m'];
+ "concept",
+ "protein",
+ "chemical",
+ "structurallyDiverse",
+ "polymer",
+ "nucleicAcid",
+ "mixture",
+ "specifiedSubstanceG1",
+ "specifiedSubstanceG2",
+ "specifiedSubstanceG3",
+ "specifiedSubstanceG4m",
+ ];
imported = false;
forceChange = false;
sameSubstance = false;
@@ -97,22 +118,23 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
json: SubstanceDetail;
downloadJsonHref: any;
jsonFileName: string;
- showHeaderBar = 'true';
- showFormReadOnly = 'false';
- showRegisterEditTitle = 'true';
+ showHeaderBar = "true";
+ showFormReadOnly = "false";
+ showRegisterEditTitle = "true";
ssg4mSyntheticPathway: Ssg4mSyntheticPathway;
- saveDelayedMessage = '';
+ saveDelayedMessage = "";
isCancelBtnClicked = false;
isSavedSuccessful = false;
public configSettingsDisplay = {};
configSsg4Form: any;
configSettingReferences = false;
private submitSubscription: any = null;
+ ssg4mExportSvg: boolean;
private jsLibScriptUrls = [
- `${environment.baseHref || ''}assets/pathway/cola.min.js`,
- `${environment.baseHref || ''}assets/pathway/d3v4.js`,
- `${environment.baseHref || ''}assets/pathway/pathwayviz.js`
+ `${environment.baseHref || ""}assets/pathway/cola.min.js`,
+ `${environment.baseHref || ""}assets/pathway/d3v4.js`,
+ `${environment.baseHref || ""}assets/pathway/pathwayviz.js`,
];
constructor(
@@ -130,16 +152,21 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
private authService: AuthService,
private titleService: Title,
private configService: ConfigService,
- private sanitizer: DomSanitizer
- ) {
- }
+ private sanitizer: DomSanitizer,
+ ) {}
ngOnInit() {
- this.showHeaderBar = this.activatedRoute.snapshot.queryParams['header'] || 'true';
- this.showFormReadOnly = this.activatedRoute.snapshot.queryParams['readonly'] || 'false';
+ this.showHeaderBar =
+ this.activatedRoute.snapshot.queryParams["header"] || "true";
+ this.showFormReadOnly =
+ this.activatedRoute.snapshot.queryParams["readonly"] || "false";
this.loadingService.setLoading(true);
+ this.isAdmin = this.authService.hasRoles("admin");
+ this.isUpdater = this.authService.hasAnyRoles("Updater", "SuperUpdater");
+ this.isAuthenticated = this.authService.getUser() !== "";
this.overlayContainer = this.overlayContainerService.getContainerElement();
this.imported = false;
+ this.ssg4mExportSvg = this.configService.configData.ssg4mExportSvg || false;
this.getConfigSettings();
if (this.configSsg4Form) {
@@ -147,97 +174,140 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.showRegisterEditTitle = this.configSsg4Form.showRegisterEditTitle;
}
- this.substanceClass = 'specifiedSubstanceG4m';
-
- const routeSubscription = this.activatedRoute
- .params
- .subscribe(params => {
- if (params['id']) {
- const id = params['id'];
- if (id !== this.id) {
- this.id = id;
- this.gaService.sendPageView(`Substance Edit`);
- this.titleService.setTitle('Edit - Specified Substance Group 4 Manufacturing');
- const newType = this.activatedRoute.snapshot.queryParamMap.get('switch') || null;
- if (newType) {
- this.getSsg4mDetails(newType);
- } else {
- this.getSsg4mDetails();
- }
- } // if Id
- } else if (this.activatedRoute.snapshot.queryParams['action']) { // import in new register form
- const actionParam = this.activatedRoute.snapshot.queryParams['action'] || null;
- if (actionParam && actionParam === 'import' && window.history.state) {
- const record = window.history.state.record;
- if ((record) && this.jsonValid(record)) {
- const responseImport = JSON.parse(record);
- if (responseImport) {
- this.substanceFormService.loadSubstance(this.substanceClass, responseImport).pipe(take(1)).subscribe(() => {
+ this.substanceClass = "specifiedSubstanceG4m";
+
+ const routeSubscription = this.activatedRoute.params.subscribe((params) => {
+ if (params["id"]) {
+ const id = params["id"];
+ if (id !== this.id) {
+ this.id = id;
+ this.gaService.sendPageView(`Substance Edit`);
+ this.titleService.setTitle(
+ "Edit - Specified Substance Group 4 Manufacturing",
+ );
+ const newType =
+ this.activatedRoute.snapshot.queryParamMap.get("switch") || null;
+ if (newType) {
+ this.getSsg4mDetails(newType);
+ } else {
+ this.getSsg4mDetails();
+ }
+ } // if Id
+ } else if (this.activatedRoute.snapshot.queryParams["action"]) {
+ // import in new register form
+ const actionParam =
+ this.activatedRoute.snapshot.queryParams["action"] || null;
+ if (actionParam && actionParam === "import" && window.history.state) {
+ const record = window.history.state.record;
+ if (record && this.jsonValid(record)) {
+ const responseImport = JSON.parse(record);
+ if (responseImport) {
+ this.substanceFormService
+ .loadSubstance(this.substanceClass, responseImport)
+ .pipe(take(1))
+ .subscribe(() => {
this.setFormSections(formSections[this.substanceClass]);
setTimeout(() => {
this.forceChange = true;
this.dynamicComponents.forEach((cRef, index) => {
this.dynamicComponentLoader
- .getComponentFactory(this.formSections[index].dynamicComponentName)
- .subscribe(componentFactory => {
- this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory);
- this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex);
- this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => {
- this.formSections[index].menuLabel = label;
- });
- const hiddenStateSubscription =
- this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => {
- this.formSections[index].isHidden = isHidden;
+ .getComponentFactory(
+ this.formSections[index].dynamicComponentName,
+ )
+ .subscribe((componentFactory) => {
+ this.formSections[index].dynamicComponentRef =
+ cRef.createComponent(componentFactory);
+ this.formSections[index].matExpansionPanel =
+ this.matExpansionPanels.find(
+ (item, panelIndex) => index === panelIndex,
+ );
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.menuLabelUpdate
+ .pipe(take(1))
+ .subscribe((label) => {
+ this.formSections[index].menuLabel = label;
});
+ const hiddenStateSubscription = this.formSections[
+ index
+ ].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(
+ (isHidden) => {
+ this.formSections[index].isHidden = isHidden;
+ },
+ );
this.subscriptions.push(hiddenStateSubscription);
- this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => {
- this.formSections[index].canAddItem = isList;
- if (isList) {
- const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => {
- this.formSections[index].matExpansionPanel.open();
- this.formSections[index].dynamicComponentRef.instance.addItem();
- });
- this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => {
- aieSubscription.unsubscribe();
- });
- }
- });
- this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges();
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.canAddItemUpdate
+ .pipe(take(1))
+ .subscribe((isList) => {
+ this.formSections[index].canAddItem = isList;
+ if (isList) {
+ const aieSubscription = this.formSections[
+ index
+ ].addItemEmitter.subscribe(() => {
+ this.formSections[
+ index
+ ].matExpansionPanel.open();
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.addItem();
+ });
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.componentDestroyed
+ .pipe(take(1))
+ .subscribe(() => {
+ aieSubscription.unsubscribe();
+ });
+ }
+ });
+ this.formSections[
+ index
+ ].dynamicComponentRef.changeDetectorRef.detectChanges();
});
});
});
- }); // load Substance in Import on new Register page
- }
+ }); // load Substance in Import on new Register page
}
}
+ }
- this.loadingService.setLoading(false);
- this.isLoading = false;
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
- // this.imported = true;
- // this.getDetailsFromImport(record.record);
- /* } else {
+ // this.imported = true;
+ // this.getDetailsFromImport(record.record);
+ /* } else {
this.copy = this.activatedRoute.snapshot.queryParams['copy'] || null;
if (this.copy) {
const copyType = this.activatedRoute.snapshot.queryParams['copyType'] || null;
this.getPartialSubstanceDetails(this.copy, copyType);
this.gaService.sendPageView(`Substance Register`);
} */
- } else { // new record
- setTimeout(() => {
- this.gaService.sendPageView(`Substance Register`);
- this.subClass = this.activatedRoute.snapshot.params['type'] || 'specifiedSubstanceG4m';
- this.substanceClass = this.subClass;
- this.titleService.setTitle('Register - Specified Substance Group 4 Manufacturing');
- this.substanceFormService.loadSubstance(this.substanceClass).pipe(take(1)).subscribe(() => {
+ } else {
+ // new record
+ setTimeout(() => {
+ this.gaService.sendPageView(`Substance Register`);
+ this.subClass =
+ this.activatedRoute.snapshot.params["type"] ||
+ "specifiedSubstanceG4m";
+ this.substanceClass = this.subClass;
+ this.titleService.setTitle(
+ "Register - Specified Substance Group 4 Manufacturing",
+ );
+ this.substanceFormService
+ .loadSubstance(this.substanceClass)
+ .pipe(take(1))
+ .subscribe(() => {
this.setFormSections(formSections[this.substanceClass]);
this.loadingService.setLoading(false);
this.isLoading = false;
});
- });
- } //else
- });
+ });
+ } //else
+ });
this.subscriptions.push(routeSubscription);
const routerSubscription = this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
@@ -264,84 +334,112 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
});
*/
// Scheme View loading
- if (!window['schemeUtil']) {
- for (let i = 0; i < this.jsLibScriptUrls.length; i++) {
- const node = document.createElement('script');
- node.src = this.jsLibScriptUrls[i];
- node.type = 'text/javascript';
- node.async = false;
- document.getElementsByTagName('head')[0].appendChild(node);
- }
- }
+ // if (!window['schemeUtil']) {
+ // for (let i = 0; i < this.jsLibScriptUrls.length; i++) {
+ // const node = document.createElement('script');
+ // node.src = this.jsLibScriptUrls[i];
+ // node.type = 'text/javascript';
+ // node.async = false;
+ // document.getElementsByTagName('head')[0].appendChild(node);
+ // }
+ // }
}
ngAfterViewInit(): void {
- const subscription = this.dynamicComponents.changes
- .subscribe(() => {
-
- const total = this.formSections.length;
- let finished = 0;
- if (!this.forceChange) {
- this.loadingService.setLoading(true);
- const startTime = new Date();
- this.dynamicComponents.forEach((cRef, index) => {
- this.dynamicComponentLoader
- .getComponentFactory(this.formSections[index].dynamicComponentName)
- .subscribe(componentFactory => {
- this.loadingService.setLoading(true);
- this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory);
- this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex);
- this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => {
+ const subscription = this.dynamicComponents.changes.subscribe(() => {
+ const total = this.formSections.length;
+ let finished = 0;
+ if (!this.forceChange) {
+ this.loadingService.setLoading(true);
+ const startTime = new Date();
+ this.dynamicComponents.forEach((cRef, index) => {
+ this.dynamicComponentLoader
+ .getComponentFactory(
+ this.formSections[index].dynamicComponentName,
+ )
+ .subscribe((componentFactory) => {
+ this.loadingService.setLoading(true);
+ this.formSections[index].dynamicComponentRef =
+ cRef.createComponent(componentFactory);
+ this.formSections[index].matExpansionPanel =
+ this.matExpansionPanels.find(
+ (item, panelIndex) => index === panelIndex,
+ );
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.menuLabelUpdate
+ .pipe(take(1))
+ .subscribe((label) => {
this.formSections[index].menuLabel = label;
});
- const hiddenStateSubscription =
- this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => {
- this.formSections[index].isHidden = isHidden;
- });
- this.subscriptions.push(hiddenStateSubscription);
- this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => {
+ const hiddenStateSubscription = this.formSections[
+ index
+ ].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(
+ (isHidden) => {
+ this.formSections[index].isHidden = isHidden;
+ },
+ );
+ this.subscriptions.push(hiddenStateSubscription);
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.canAddItemUpdate
+ .pipe(take(1))
+ .subscribe((isList) => {
this.formSections[index].canAddItem = isList;
if (isList) {
- const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => {
+ const aieSubscription = this.formSections[
+ index
+ ].addItemEmitter.subscribe(() => {
this.formSections[index].matExpansionPanel.open();
- this.formSections[index].dynamicComponentRef.instance.addItem();
- });
- this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => {
- aieSubscription.unsubscribe();
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.addItem();
});
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.componentDestroyed
+ .pipe(take(1))
+ .subscribe(() => {
+ aieSubscription.unsubscribe();
+ });
}
});
- this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges();
- finished++;
- if (finished >= total) {
- this.loadingService.setLoading(false);
- } else {
- const currentTime = new Date();
- if (currentTime.getTime() - startTime.getTime() > 12000) {
- if (confirm('There was a network error while fetching files, would you like to refresh?')) {
- window.location.reload();
- }
+ this.formSections[
+ index
+ ].dynamicComponentRef.changeDetectorRef.detectChanges();
+ finished++;
+ if (finished >= total) {
+ this.loadingService.setLoading(false);
+ } else {
+ const currentTime = new Date();
+ if (currentTime.getTime() - startTime.getTime() > 12000) {
+ if (
+ confirm(
+ "There was a network error while fetching files, would you like to refresh?",
+ )
+ ) {
+ window.location.reload();
}
}
- setTimeout(() => {
- this.loadingService.setLoading(false);
- // this.UNII = this.substanceSsg4mService.getUNII();
- }, 5);
- });
- });
- // this.loadingService.setLoading(false);
-
- }
- subscription.unsubscribe();
- });
+ }
+ setTimeout(() => {
+ this.loadingService.setLoading(false);
+ // this.UNII = this.substanceSsg4mService.getUNII();
+ }, 5);
+ });
+ });
+ // this.loadingService.setLoading(false);
+ }
+ subscription.unsubscribe();
+ });
}
private setFormSections(sectionNames: Array = []): void {
this.formSections = [];
- sectionNames.forEach(sectionName => {
+ sectionNames.forEach((sectionName) => {
let canAdd = true;
if (this.configSettingReferences === false) {
- if (sectionName === 'substance-form-references') {
+ if (sectionName === "substance-form-references") {
canAdd = false;
}
}
@@ -355,24 +453,27 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
getConfigSettings(): void {
// Get SSG4 Config Settings from config.json file to show and hide fields in the form
// let configSsg4Form: any;
- this.configSsg4Form = this.configService.configData && this.configService.configData.ssg4Form || null;
+ this.configSsg4Form =
+ (this.configService.configData &&
+ this.configService.configData.ssg4Form) ||
+ null;
// *** IMPORTANT: get the correct value. Get 'startingMaterial' json values from config
let configReferences = this.configSsg4Form.settingsDisplay.references;
this.configSettingReferences = false;
- if (configReferences === 'simple') {
+ if (configReferences === "simple") {
this.configSettingReferences = true;
- } else if (configReferences === 'advanced') {
+ } else if (configReferences === "advanced") {
this.configSettingReferences = false;
- } else if (configReferences === 'removed') {
+ } else if (configReferences === "removed") {
this.configSettingReferences = false;
}
}
openedChange(event: any) {
if (event) {
- this.overlayContainer.style.zIndex = '1002';
+ this.overlayContainer.style.zIndex = "1002";
} else {
- this.overlayContainer.style.zIndex = '1000';
+ this.overlayContainer.style.zIndex = "1000";
}
}
@@ -449,33 +550,35 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
ngOnDestroy(): void {
// this.substanceFormService.unloadSubstance();
- this.subscriptions.forEach(subscription => {
+ this.subscriptions.forEach((subscription) => {
subscription.unsubscribe();
});
}
-
importDialog(): void {
let data: any;
data = {
- title: 'Manufacturing Scheme Import'
+ title: "Manufacturing Scheme Import",
};
const dialogRef = this.dialog.open(SubstanceEditImportDialogComponent, {
- width: '650px',
+ width: "650px",
autoFocus: false,
- data: data
+ data: data,
});
- this.overlayContainer.style.zIndex = '1002';
+ this.overlayContainer.style.zIndex = "1002";
- const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => {
- if (response) {
- this.loadingService.setLoading(true);
- this.overlayContainer.style.zIndex = null;
+ const dialogSubscription = dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((response) => {
+ if (response) {
+ this.loadingService.setLoading(true);
+ this.overlayContainer.style.zIndex = null;
- // attempting to reload a substance without a router refresh has proven to cause issues with the relationship dropdowns
- // There are probably other components affected. There is an issue with subscriptions likely due to some OnInit not firing
+ // attempting to reload a substance without a router refresh has proven to cause issues with the relationship dropdowns
+ // There are probably other components affected. There is an issue with subscriptions likely due to some OnInit not firing
- /* const read = JSON.parse(response);
+ /* const read = JSON.parse(response);
if (this.id && read.uuid && this.id === read.uuid) {
this.substanceFormService.importSubstance(read, 'update');
this.submissionMessage = null;
@@ -493,20 +596,30 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.loadingService.setLoading(false);
this.isLoading = false;
} else {*/
- setTimeout(() => {
- this.router.onSameUrlNavigation = 'reload';
- this.loadingService.setLoading(false);
- if (this.id) {
- this.router.navigateByUrl('/substances-ssg4m/' + this.id + '/edit?action=import&header=' + this.showHeaderBar, { state: { record: response } });
- } else { // new record
- this.router.navigateByUrl('/substances-ssg4m/register?action=import&header=' + this.showHeaderBar, { state: { record: response } });
- }
- }, 1000);
- }
- // }
- // }
- });
-
+ setTimeout(() => {
+ this.router.onSameUrlNavigation = "reload";
+ this.loadingService.setLoading(false);
+ if (this.id) {
+ this.router.navigateByUrl(
+ "/substances-ssg4m/" +
+ this.id +
+ "/edit?action=import&header=" +
+ this.showHeaderBar,
+ { state: { record: response } },
+ );
+ } else {
+ // new record
+ this.router.navigateByUrl(
+ "/substances-ssg4m/register?action=import&header=" +
+ this.showHeaderBar,
+ { state: { record: response } },
+ );
+ }
+ }, 1000);
+ }
+ // }
+ // }
+ });
}
test() {
@@ -515,8 +628,8 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
}
canBeApproved(): boolean {
- const action = this.activatedRoute.snapshot.queryParams['action'] || null;
- if (action && action === 'import') {
+ const action = this.activatedRoute.snapshot.queryParams["action"] || null;
+ if (action && action === "import") {
return false;
}
if (this.definition && this.definition.lastEditedBy && this.user) {
@@ -524,171 +637,309 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
if (!lastEdit) {
return false;
}
- if (this.definition.status === 'approved') {
+ if (this.definition.status === "approved") {
return false;
}
if (lastEdit === this.user) {
return false;
}
return true;
-
}
return false;
}
showJSON(): void {
+ const json = this.substanceFormService.cleanSubstance();
+ this.removeTmpStructureIdFields(json);
const dialogRef = this.dialog.open(JsonDialogComponent, {
- width: '90%'
+ width: "90%",
+ data: { substance: json },
});
- this.overlayContainer.style.zIndex = '1002';
-
- const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe(response => {
+ this.overlayContainer.style.zIndex = "1002";
- });
+ const dialogSubscription = dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((response) => {});
this.subscriptions.push(dialogSubscription);
}
saveJSON(): void {
// apply the same cleaning to remove deleted objects and return what will be sent to the server on validation / submission
this.json = this.substanceFormService.cleanSubstance();
- // this.json = this.cleanObject(substanceCopy);
- const uri = this.sanitizer.bypassSecurityTrustUrl('data:text/json;charset=UTF-8,' + encodeURIComponent(JSON.stringify(this.json)));
+ // Remove $$tmpStructureId fields before export
+ this.removeTmpStructureIdFields(this.json);
+ const uri = this.sanitizer.bypassSecurityTrustUrl(
+ "data:text/json;charset=UTF-8," +
+ encodeURIComponent(JSON.stringify(this.json)),
+ );
this.downloadJsonHref = uri;
const date = new Date();
- this.jsonFileName = 'SSG4m_' + moment(date).format('MMM-DD-YYYY_H-mm-ss');
+ this.jsonFileName = "SSG4m_" + moment(date).format("MMM-DD-YYYY_H-mm-ss");
}
- checkSsg4mServerStatus(): void {
- // Check Microservice Server Status
- this.substanceSsg4mService.checkSsg4mServerStatus().pipe(take(1)).subscribe(responseCheck => {
- if (responseCheck) {
- if (responseCheck["status"]) {
- if (responseCheck["status"] === 'OK') {
- this.microserviceStatusUp = true;
- }
+ saveLocalBatch(): void {
+ const json = this.substanceFormService.cleanSubstance();
+ this.removeTmpStructureIdFields(json);
+
+ const timestamp = moment(new Date()).format("MMM-DD-YYYY_H-mm-ss");
+
+ // Download the SSG4M substance file
+ this.downloadFile(JSON.stringify(json), "SSG4m_" + timestamp + ".json");
+
+ // Collect all refuuids from materials in the SSG4M hierarchy
+ const referencedUuids = new Set();
+ if (json.specifiedSubstanceG4m && json.specifiedSubstanceG4m.process) {
+ for (const process of json.specifiedSubstanceG4m.process) {
+ if (!process.sites) {
+ continue;
}
- } else {
- this.microserviceStatusUp = false;
- }
- }, error => {
- // if it is authentication issue or status is 0, reload the current page
-
- // These lines did not work. Commenting out right now. Caused forever refresh loop.
- // if (error.status === 0) {
- // window.location.reload();
- // }
-
- if (error.status === 0) {
- console.log("Error Status is 0");
- let totalNumberRefresh = 2;
- let preventRefresh = parseInt((new URLSearchParams(window.location.search)).get("refreshcount"));
- // if parameter 'refreshcount' is NOT found in the URL, add refreshcount in the URL.
- // refresh n/totalNumberRefresh times
- if (!preventRefresh || (preventRefresh && Number(preventRefresh) < totalNumberRefresh)) {
- let url = window.location.href;
- // if question mark '?' found in the URL, add/append '&', otherwise add/append '?' in the URL
- if (!preventRefresh) { // not found preventRefresh in URL
- preventRefresh = 1;
- if (url.indexOf('?') > -1) {
- url += '&refreshcount=' + preventRefresh;
- } else {
- url += '?refreshcount=' + preventRefresh;
+ for (const site of process.sites) {
+ if (!site.stages) {
+ continue;
+ }
+ for (const stage of site.stages) {
+ const allMaterials = [
+ ...(stage.startingMaterials || []),
+ ...(stage.processingMaterials || []),
+ ...(stage.resultingMaterials || []),
+ ];
+ for (const material of allMaterials) {
+ if (material.substanceName && material.substanceName.refuuid) {
+ referencedUuids.add(material.substanceName.refuuid);
+ }
}
- } else { // found refreshcount in the URL
- let currentCountUrl = 'refreshcount=' + preventRefresh;
- // add 1 to current count
- preventRefresh = preventRefresh + 1;
- let newCounturl = 'refreshcount=' + preventRefresh;
- // replace 'refreshcount=1' to 'refreshcount=2'
- url = url.replace(currentCountUrl, newCounturl);
}
- // Display error message to users that the page will refresh n number of times.
- this.errorMessage = "There was a connection problem loading the page. Automatically refreshing " + preventRefresh + " of " + totalNumberRefresh + " times ...
";
-
- setTimeout(() => {
- window.location.href = url;
- }, 2000);
}
}
+ }
- this.microserviceStatusUp = false;
- if (!this.errorMessage) {
- this.errorMessage = '';
- }
- this.errorMessage = this.errorMessage + "Unable to load the data for Record ID " + this.id + "
";
- if (error && error.error && error.error.message) {
- this.errorMessage = this.errorMessage + 'Server Error ' + (error.status + ': ' || ': ') + error.error.message;
- } else if (error && error.error && (typeof error.error) === 'string') {
- this.errorMessage = this.errorMessage + ' Server Error ' + (error.status + ': ' || '') + error.error;
- } else if (error && error.message) {
- this.errorMessage = this.errorMessage + ' Server Error ' + (error.status + ': ' || '') + error.message;
- this.errorMessage = this.errorMessage + " It looks like the SSG4m microservice is not running. ";
- this.errorMessage = this.errorMessage + "Please ask your system administrator to verify that the SSG4m microservice is running without error, and also to examine the website logs to: - check if there are any database connection issues, and - make sure the system is using valid authentication credentials into the database";
- }
- else {
- this.errorMessage = this.errorMessage + 'There could be an authentication issue. -Make sure that you are logged into the GSRS website. -Clear your browser cache. -Reload your SSG4 page or Appian';
+ // Find matching drafts in localStorage and download each as a separate file
+ const keys = Object.keys(localStorage);
+ for (const key of keys) {
+ if (key.startsWith("gsrs-draft-")) {
+ const draft = JSON.parse(localStorage.getItem(key));
+ if (
+ draft &&
+ draft.substance &&
+ referencedUuids.has(draft.substance.uuid)
+ ) {
+ const name = draft.substance.uuid || draft.name || "unknown";
+ const safeName = name.replace(/[^a-zA-Z0-9_-]/g, "_");
+ this.downloadFile(
+ JSON.stringify(draft.substance),
+ "Draft_" + safeName + "_" + timestamp + ".json",
+ );
+ }
}
- });
+ }
+ }
+
+ private downloadFile(content: string, fileName: string): void {
+ const blob = new Blob([content], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = fileName;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ }
+
+ checkSsg4mServerStatus(): void {
+ // Check Microservice Server Status
+ this.substanceSsg4mService
+ .checkSsg4mServerStatus()
+ .pipe(take(1))
+ .subscribe(
+ (responseCheck) => {
+ if (responseCheck) {
+ if (responseCheck["status"]) {
+ if (responseCheck["status"] === "OK") {
+ this.microserviceStatusUp = true;
+ }
+ }
+ } else {
+ this.microserviceStatusUp = false;
+ }
+ },
+ (error) => {
+ // if it is authentication issue or status is 0, reload the current page
+
+ // These lines did not work. Commenting out right now. Caused forever refresh loop.
+ // if (error.status === 0) {
+ // window.location.reload();
+ // }
+
+ if (error.status === 0) {
+ console.log("Error Status is 0");
+ let totalNumberRefresh = 2;
+ let preventRefresh = parseInt(
+ new URLSearchParams(window.location.search).get("refreshcount"),
+ );
+ // if parameter 'refreshcount' is NOT found in the URL, add refreshcount in the URL.
+ // refresh n/totalNumberRefresh times
+ if (
+ !preventRefresh ||
+ (preventRefresh && Number(preventRefresh) < totalNumberRefresh)
+ ) {
+ let url = window.location.href;
+ // if question mark '?' found in the URL, add/append '&', otherwise add/append '?' in the URL
+ if (!preventRefresh) {
+ // not found preventRefresh in URL
+ preventRefresh = 1;
+ if (url.indexOf("?") > -1) {
+ url += "&refreshcount=" + preventRefresh;
+ } else {
+ url += "?refreshcount=" + preventRefresh;
+ }
+ } else {
+ // found refreshcount in the URL
+ let currentCountUrl = "refreshcount=" + preventRefresh;
+ // add 1 to current count
+ preventRefresh = preventRefresh + 1;
+ let newCounturl = "refreshcount=" + preventRefresh;
+ // replace 'refreshcount=1' to 'refreshcount=2'
+ url = url.replace(currentCountUrl, newCounturl);
+ }
+ // Display error message to users that the page will refresh n number of times.
+ this.errorMessage =
+ "There was a connection problem loading the page. Automatically refreshing " +
+ preventRefresh +
+ " of " +
+ totalNumberRefresh +
+ " times ...
";
+
+ setTimeout(() => {
+ window.location.href = url;
+ }, 2000);
+ }
+ }
+
+ this.microserviceStatusUp = false;
+ if (!this.errorMessage) {
+ this.errorMessage = "";
+ }
+ this.errorMessage =
+ this.errorMessage +
+ "Unable to load the data for Record ID " +
+ this.id +
+ "
";
+ if (error && error.error && error.error.message) {
+ this.errorMessage =
+ this.errorMessage +
+ "Server Error " +
+ (error.status + ": " || ": ") +
+ error.error.message;
+ } else if (error && error.error && typeof error.error === "string") {
+ this.errorMessage =
+ this.errorMessage +
+ " Server Error " +
+ (error.status + ": " || "") +
+ error.error;
+ } else if (error && error.message) {
+ this.errorMessage =
+ this.errorMessage +
+ " Server Error " +
+ (error.status + ": " || "") +
+ error.message;
+ this.errorMessage =
+ this.errorMessage +
+ " It looks like the SSG4m microservice is not running. ";
+ this.errorMessage =
+ this.errorMessage +
+ "Please ask your system administrator to verify that the SSG4m microservice is running without error, and also to examine the website logs to: - check if there are any database connection issues, and - make sure the system is using valid authentication credentials into the database";
+ } else {
+ this.errorMessage =
+ this.errorMessage +
+ "There could be an authentication issue. -Make sure that you are logged into the GSRS website. -Clear your browser cache. -Reload your SSG4 page or Appian";
+ }
+ },
+ );
}
getSsg4mDetails(newType?: string): void {
- let generateErrorMessage = "Unable to load the data for Record ID " + this.id + "
";
+ let generateErrorMessage =
+ "Unable to load the data for Record ID " + this.id + "
";
// Check if SSG4m microservice server is UP or not
this.checkSsg4mServerStatus();
- this.substanceSsg4mService.getSsg4mDetails(this.id).pipe(take(1)).subscribe(response => {
- /*if (response._name) {
+ this.substanceSsg4mService
+ .getSsg4mDetails(this.id)
+ .pipe(take(1))
+ .subscribe(
+ (response) => {
+ /*if (response._name) {
this.titleService.setTitle('Edit - ' + response._name);
}*/
- if (response) {
- // Check for import in URL
- const action = this.activatedRoute.snapshot.queryParams['action'] || null;
- let substanceSsg4mFromDb: SubstanceDetail;
-
- this.ssg4mSyntheticPathway = response;
-
- if (action && action === 'import' && window.history.state) {
- // this.gaService.sendPageView(`Substance Import`);
- const record = window.history.state.record;
- // this.imported = true;
- // this.getDetailsFromImport(record.record);
- if ((record) && this.jsonValid(record)) {
- const responseImport = JSON.parse(record);
- if (responseImport) {
- this.substanceFormService.loadSubstance(this.substanceClass, responseImport).pipe(take(1)).subscribe(() => {
- // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections[this.substanceClass]);
- });
+ if (response) {
+ // Check for import in URL
+ const action =
+ this.activatedRoute.snapshot.queryParams["action"] || null;
+ let substanceSsg4mFromDb: SubstanceDetail;
+
+ this.ssg4mSyntheticPathway = response;
+
+ if (action && action === "import" && window.history.state) {
+ // this.gaService.sendPageView(`Substance Import`);
+ const record = window.history.state.record;
+ // this.imported = true;
+ // this.getDetailsFromImport(record.record);
+ if (record && this.jsonValid(record)) {
+ const responseImport = JSON.parse(record);
+ if (responseImport) {
+ this.substanceFormService
+ .loadSubstance(this.substanceClass, responseImport)
+ .pipe(take(1))
+ .subscribe(() => {
+ // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => {
+ this.setFormSections(formSections[this.substanceClass]);
+ });
+ }
+ }
+ } else if (response.sbmsnDataText) {
+ // If JSON form data found into the database, load into the form
+ substanceSsg4mFromDb = JSON.parse(response.sbmsnDataText);
+
+ this.substanceFormService
+ .loadSubstance(
+ substanceSsg4mFromDb.substanceClass,
+ substanceSsg4mFromDb,
+ )
+ .pipe(take(1))
+ .subscribe(() => {
+ this.setFormSections(formSections[this.substanceClass]);
+ });
+ } else if (!response.sbmsnDataText) {
+ // AS NEW FORM, If JSON form data NOT found into the database, Load the Form as a new Form
+ this.substanceFormService
+ .loadSubstance(this.substanceClass)
+ .pipe(take(1))
+ .subscribe(() => {
+ // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => {
+ this.setFormSections(formSections[this.substanceClass]);
+ });
}
+ } else if (response === null) {
+ this.errorMessage =
+ "There is no data found in the database for ID: " + this.id;
}
- }
- else if (response.sbmsnDataText) { // If JSON form data found into the database, load into the form
- substanceSsg4mFromDb = JSON.parse(response.sbmsnDataText);
-
- this.substanceFormService.loadSubstance(substanceSsg4mFromDb.substanceClass, substanceSsg4mFromDb).pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections[this.substanceClass]);
- });
- } else if (!response.sbmsnDataText) { // AS NEW FORM, If JSON form data NOT found into the database, Load the Form as a new Form
- this.substanceFormService.loadSubstance(this.substanceClass).pipe(take(1)).subscribe(() => {
- // this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections[this.substanceClass]);
- });
- }
- } else if (response === null) {
- this.errorMessage = "There is no data found in the database for ID: " + this.id;
- }
- this.loadingService.setLoading(false);
- this.isLoading = false;
- }, error => { // Getting Error while getting Record
- if (this.microserviceStatusUp === false) {
- }
- this.gaService.sendException('getSsg4mDetails: error from API call');
- this.loadingService.setLoading(false);
- this.isLoading = false;
- // this.handleSubstanceRetrivalError();
- });
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ },
+ (error) => {
+ // Getting Error while getting Record
+ if (this.microserviceStatusUp === false) {
+ }
+ this.gaService.sendException("getSsg4mDetails: error from API call");
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ // this.handleSubstanceRetrivalError();
+ },
+ );
}
jsonValid(file: any): boolean {
@@ -707,103 +958,149 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.definitionType = response.definitionType;
this.substanceClass = response.substanceClass;
this.status = response.status;
- this.substanceFormService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => {
- // this.substanceSsg4mService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections[response.substanceClass]);
- if (!same) {
- setTimeout(() => {
- this.forceChange = true;
- this.dynamicComponents.forEach((cRef, index) => {
- this.dynamicComponentLoader
- .getComponentFactory(this.formSections[index].dynamicComponentName)
- .subscribe(componentFactory => {
- this.formSections[index].dynamicComponentRef = cRef.createComponent(componentFactory);
- this.formSections[index].matExpansionPanel = this.matExpansionPanels.find((item, panelIndex) => index === panelIndex);
- this.formSections[index].dynamicComponentRef.instance.menuLabelUpdate.pipe(take(1)).subscribe(label => {
- this.formSections[index].menuLabel = label;
- });
- const hiddenStateSubscription =
- this.formSections[index].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(isHidden => {
- this.formSections[index].isHidden = isHidden;
+ this.substanceFormService
+ .loadSubstance(response.substanceClass, response, "import")
+ .pipe(take(1))
+ .subscribe(
+ () => {
+ // this.substanceSsg4mService.loadSubstance(response.substanceClass, response, 'import').pipe(take(1)).subscribe(() => {
+ this.setFormSections(formSections[response.substanceClass]);
+ if (!same) {
+ setTimeout(() => {
+ this.forceChange = true;
+ this.dynamicComponents.forEach((cRef, index) => {
+ this.dynamicComponentLoader
+ .getComponentFactory(
+ this.formSections[index].dynamicComponentName,
+ )
+ .subscribe((componentFactory) => {
+ this.formSections[index].dynamicComponentRef =
+ cRef.createComponent(componentFactory);
+ this.formSections[index].matExpansionPanel =
+ this.matExpansionPanels.find(
+ (item, panelIndex) => index === panelIndex,
+ );
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.menuLabelUpdate
+ .pipe(take(1))
+ .subscribe((label) => {
+ this.formSections[index].menuLabel = label;
+ });
+ const hiddenStateSubscription = this.formSections[
+ index
+ ].dynamicComponentRef.instance.hiddenStateUpdate.subscribe(
+ (isHidden) => {
+ this.formSections[index].isHidden = isHidden;
+ },
+ );
+ this.subscriptions.push(hiddenStateSubscription);
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.canAddItemUpdate
+ .pipe(take(1))
+ .subscribe((isList) => {
+ this.formSections[index].canAddItem = isList;
+ if (isList) {
+ const aieSubscription = this.formSections[
+ index
+ ].addItemEmitter.subscribe(() => {
+ this.formSections[index].matExpansionPanel.open();
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.addItem();
+ });
+ this.formSections[
+ index
+ ].dynamicComponentRef.instance.componentDestroyed
+ .pipe(take(1))
+ .subscribe(() => {
+ aieSubscription.unsubscribe();
+ });
+ }
+ });
+ this.formSections[
+ index
+ ].dynamicComponentRef.changeDetectorRef.detectChanges();
});
- this.subscriptions.push(hiddenStateSubscription);
- this.formSections[index].dynamicComponentRef.instance.canAddItemUpdate.pipe(take(1)).subscribe(isList => {
- this.formSections[index].canAddItem = isList;
- if (isList) {
- const aieSubscription = this.formSections[index].addItemEmitter.subscribe(() => {
- this.formSections[index].matExpansionPanel.open();
- this.formSections[index].dynamicComponentRef.instance.addItem();
- });
- this.formSections[index].dynamicComponentRef.instance.componentDestroyed.pipe(take(1)).subscribe(() => {
- aieSubscription.unsubscribe();
- });
- }
- });
- this.formSections[index].dynamicComponentRef.changeDetectorRef.detectChanges();
});
- });
- this.canApprove = false;
- });
- }
- }, error => {
- this.loadingService.setLoading(false);
- });
+ this.canApprove = false;
+ });
+ }
+ },
+ (error) => {
+ this.loadingService.setLoading(false);
+ },
+ );
} else {
this.handleSubstanceRetrivalError();
this.loadingService.setLoading(false);
-
}
this.loadingService.setLoading(false);
this.isLoading = false;
}
getPartialSubstanceDetails(uuid: string, type: string): void {
- this.substanceService.getSubstanceDetails(uuid).pipe(take(1)).subscribe(response => {
- if (response) {
- this.substanceClass = response.substanceClass;
- this.status = response.status;
- delete response.uuid;
- if (response._name) {
- delete response._name;
- }
- this.scrub(response, type);
- this.substanceSsg4mService.loadSubstance(response.substanceClass, response).pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections[response.substanceClass]);
+ this.substanceService
+ .getSubstanceDetails(uuid)
+ .pipe(take(1))
+ .subscribe(
+ (response) => {
+ if (response) {
+ this.substanceClass = response.substanceClass;
+ this.status = response.status;
+ delete response.uuid;
+ if (response._name) {
+ delete response._name;
+ }
+ this.scrub(response, type);
+ this.substanceSsg4mService
+ .loadSubstance(response.substanceClass, response)
+ .pipe(take(1))
+ .subscribe(() => {
+ this.setFormSections(formSections[response.substanceClass]);
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ });
+ } else {
+ this.handleSubstanceRetrivalError();
+ }
+ },
+ (error) => {
+ this.gaService.sendException(
+ "getSubstanceDetails: error from API call",
+ );
this.loadingService.setLoading(false);
this.isLoading = false;
- });
- } else {
- this.handleSubstanceRetrivalError();
- }
- }, error => {
- this.gaService.sendException('getSubstanceDetails: error from API call');
- this.loadingService.setLoading(false);
- this.isLoading = false;
- this.handleSubstanceRetrivalError();
- });
+ this.handleSubstanceRetrivalError();
+ },
+ );
}
private handleSubstanceRetrivalError() {
const notification: AppNotification = {
- message: 'The substance you\'re trying to edit doesn\'t exist.',
+ message: "The substance you're trying to edit doesn't exist.",
type: NotificationType.error,
- milisecondsToShow: 4000
+ milisecondsToShow: 4000,
};
this.mainNotificationService.setNotification(notification);
this.errorMessage = "Unable to load the data.";
setTimeout(() => {
- this.router.navigate(['/home']);
- this.substanceSsg4mService.loadSubstance(this.subClass).pipe(take(1)).subscribe(() => {
- this.setFormSections(formSections.chemical);
- this.loadingService.setLoading(false);
- this.isLoading = false;
- });
+ this.router.navigate(["/home"]);
+ this.substanceSsg4mService
+ .loadSubstance(this.subClass)
+ .pipe(take(1))
+ .subscribe(() => {
+ this.setFormSections(formSections.chemical);
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ });
}, 5000);
}
- validate(validationType?: string): void {
- if (validationType && validationType === 'approval') {
+ async validate(validationType?: string): Promise {
+ if (validationType && validationType === "approval") {
this.approving = true;
} else {
this.approving = false;
@@ -822,7 +1119,7 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.isLoading = false;
// If there is no validation error, submit/save the records without displaying the warning/validation message.
if (this.validationMessages.length === 0 && true === true) {
- this.submit();
+ await this.submit();
}
/*
if (this.validationMessages.length === 0 && true === true) {
@@ -869,7 +1166,7 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
*/
validateSubstance(): Observable {
- return new Observable(observer => {
+ return new Observable((observer) => {
// const substanceCopy = this.cleanSubstance();
// CHANGING THIS NOW CHANGING THIS NOW
const substanceCopy = null;
@@ -971,14 +1268,176 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
}
observer.next(results);
observer.complete();
- }, error => {
+ },
+ (error) => {
observer.error();
observer.complete();
});
});
}
- submit(): void {
+ getPageStyles(): string {
+ let css = "";
+ // Gather all style rules from the document
+ for (const sheet of Array.from(document.styleSheets)) {
+ try {
+ if (sheet.cssRules) {
+ css += Array.from(sheet.cssRules)
+ .map((rule) => rule.cssText)
+ .join("\n");
+ }
+ } catch (e) {
+ console.warn("Cannot read styles from cross-origin stylesheet", e);
+ }
+ }
+ return ``;
+ }
+
+ delay(ms: number): Promise {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+
+ generateTimestampId(): string {
+ const now = new Date();
+
+ const pad = (num: number, length: number = 2): string =>
+ String(num).padStart(length, "0");
+
+ const datePart = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(
+ now.getDate(),
+ )}`;
+ const timePart = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(
+ now.getSeconds(),
+ )}`;
+
+ return `${datePart}${timePart}`;
+ }
+
+ private removeTmpStructureIdFields(obj: any): void {
+ if (!obj || typeof obj !== "object") {
+ return;
+ }
+ if (Array.isArray(obj)) {
+ for (const item of obj) {
+ this.removeTmpStructureIdFields(item);
+ }
+ return;
+ }
+ for (const key of Object.keys(obj)) {
+ if (key === "$$tmpStructureId") {
+ try {
+ delete obj[key];
+ } catch (e) {
+ // ignore
+ }
+ continue;
+ }
+ const val = obj[key];
+ if (val && typeof val === "object") {
+ this.removeTmpStructureIdFields(val);
+ }
+ }
+ }
+
+ // waitForElement(selector: string, timeout = 2000): Promise {
+ // return new Promise((resolve, reject) => {
+ // const interval = setInterval(() => {
+ // const element = document.querySelector(selector) as HTMLElement;
+ // if (element) {
+ // clearInterval(interval);
+ // resolve(element);
+ // }
+ // }, 100);
+ // setTimeout(() => {
+ // clearInterval(interval);
+ // reject(new Error(`Element "${selector}" not found within ${timeout}ms.`));
+ // }, timeout);
+ // });
+ // }
+
+ async expandStepView(): Promise {
+ const tabProcesses = document.querySelector(
+ "#substance-form-ssg4m-process",
+ ) as HTMLElement;
+ if (tabProcesses.getAttribute("aria-expanded") !== "true") {
+ console.log("Tab Processes not selected. Clicking it...");
+ tabProcesses.click();
+ await this.delay(200);
+ }
+
+ const allTabs = document.querySelectorAll(".mat-mdc-tab");
+ const tabStepView = Array.from(allTabs).find(
+ (tab) => tab.textContent.trim() === "Step View",
+ ) as HTMLElement;
+ if (tabStepView.getAttribute("aria-selected") !== "true") {
+ console.log("Tab Step View not selected. Clicking it...");
+ tabStepView.click();
+ await this.delay(200);
+ }
+ }
+
+ async exportStepView(document: Document): Promise {
+ const elementToConvert = document.querySelector(
+ "app-ssg4m-scheme-view",
+ ) as HTMLElement;
+ const clone = elementToConvert.cloneNode(true) as HTMLElement;
+ const initialWidth = elementToConvert.offsetWidth;
+ const initialHeight = elementToConvert.offsetHeight;
+
+ const container = document.createElement("div");
+ container.style.position = "absolute";
+ container.style.left = "-9999px";
+ container.style.padding = "0";
+ container.style.margin = "0";
+ container.style.display = "inline-block";
+ container.style.width = `${initialWidth}px`;
+ container.style.height = `${initialHeight}px`;
+ const styles = this.getPageStyles();
+ container.innerHTML = styles;
+ container.appendChild(clone);
+ document.body.appendChild(container);
+ clone.style.overflow = "visible";
+ clone.style.maxWidth = "none";
+ clone.style.boxSizing = "border-box";
+ clone.style.fontFamily = "Arial, sans-serif";
+ clone.style.display = "flex";
+ clone.style.flexDirection = "column";
+ clone.style.alignItems = "stretch";
+
+ await this.delay(2500);
+
+ const finalWidth = initialWidth;
+ const finalHeight = initialHeight;
+
+ function filter(node: HTMLElement) {
+ if (!node.tagName) {
+ return true;
+ }
+
+ return node.tagName.toLowerCase() !== "button";
+ }
+ const options = {
+ filter: filter,
+ width: finalWidth,
+ height: finalHeight,
+ fetchRequestInit: {
+ headers: new Headers(),
+ mode: "cors" as RequestMode,
+ cache: "default" as RequestCache,
+ },
+ };
+
+ const dataUrl = await toSvg(clone, options);
+ const downloadLink = document.createElement("a");
+ downloadLink.href = dataUrl;
+ downloadLink.download = `ssg4m_step_view_${this.generateTimestampId()}.svg`;
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+ document.body.removeChild(downloadLink);
+ }
+
+ async submit(): Promise {
+ this.ssg4mExportSvg && await this.expandStepView();
this.isLoading = true;
this.loadingService.setLoading(true);
this.approving = false;
@@ -986,103 +1445,272 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.json = this.substanceFormService.cleanSubstance();
let jsonValue = JSON.stringify(this.json);
- // Save SVG/Image as Blob
- //This is a hacky placeholder way to force viz
- //TODO finish this
- const ssgjs = JSON.stringify(this.substanceFormService.cleanSubstance());
- window["schemeUtil"].onFinishedLayout = (svg) => {
- window["schemeUtil"].onFinishedLayout = (svg) => { };
+ // Initialize pathway object if new record
+ if (this.ssg4mSyntheticPathway == null) {
+ this.ssg4mSyntheticPathway = {};
+ }
+
+ // Process and validate local drafts referenced by this form
+ const hasValidationErrors = await this.processLocalDrafts(jsonValue);
+ if (hasValidationErrors) {
+ return;
+ }
- // if New Record, initialize object
- if (this.ssg4mSyntheticPathway == null) {
- this.ssg4mSyntheticPathway = {};
+ setTimeout(() => {
+ if (this.isSavedSuccessful === false) {
+ this.saveDelayedMessage =
+ "Hmm ... this seems to be taking longer than normal, there may be network issues. Click here to cancel and continue working on the form. We suggest you save a local copy of the JSON.";
}
+ }, 8000);
- // Existing Record
- // get the JSON from the SSG4m Form and store as a Clob into the database
- this.ssg4mSyntheticPathway.sbmsnDataText = jsonValue;
+ // Export step view as SVG; default disabled
+ this.ssg4mExportSvg && await this.exportStepView(document);
- // Save SVG as Clob
- this.ssg4mSyntheticPathway.sbmsnImage = document.querySelector("#scheme-viz-view").innerHTML;
+ // Prepare final JSON and call save endpoint
+ jsonValue = this.prepareFinalJson();
+ this.ssg4mSyntheticPathway.sbmsnDataText = jsonValue;
- // After submitting Save button, the UI waits for 5 seconds to see if it gets a response.
- // after 5 seconds it displays a warning on the top of the UI form.
- setTimeout(() => {
- if (this.isSavedSuccessful === false) {
- this.saveDelayedMessage = "Hmm ... this seems to be taking longer than normal, there may be network issues. Click here to cancel and continue working on the form. We suggest you save a local copy of the JSON.";
- }
- }, 5000);
+ this.savePathway();
+ }
- this.submitSubscription = this.substanceSsg4mService.saveSsg4m(this.ssg4mSyntheticPathway).pipe(take(1)).subscribe(response => {
- // Stop the spinner
- this.loadingService.setLoading(false);
- this.isLoading = false;
+ // Processes local drafts from localStorage that are referenced by the G4 form.
+ // Validates and saves each referenced draft.
+ // returns true if there are validation errors that should stop submission, false otherwise
+ private async processLocalDrafts(jsonStr: string): Promise {
+ const draftKeys = Object.keys(localStorage).filter((k) =>
+ k.startsWith("gsrs-draft-"),
+ );
- // Set validation messages to null
- this.validationMessages = null;
- this.showSubmissionMessages = false;
- this.validationResult = false;
+ this.validationMessages = [];
- // if Saved Successfully
- if (response && response.synthPathwaySkey) {
- if (response.synthPathwaySkey) {
- this.id = response.synthPathwaySkey.toString();
- }
+ for (const key of draftKeys) {
+ await this.processSingleDraft(key, jsonStr);
+ }
- // if the API communication does resolve, AND the initial save went through, it will replace
- // the warning message. Only show this message when user clicked on the 'Cancel' button.
- // After user clicks 'Refresh' button, refresh the page manually.
- this.isSavedSuccessful = true;
- if (this.isCancelBtnClicked === true && this.isSavedSuccessful === true) {
- this.saveDelayedMessage = " Network communication restored, click here to refresh with saved version.";
- }
- else {
- this.saveDelayedMessage = "";
- this.isCancelBtnClicked = false;
- // Only show successful dialog and refresh page, if user does not click on the cancel button.
- this.openSuccessDialog();
- }
- // Refresh the current page, this will not cause record locking issue
- /* this.router.routeReuseStrategy.shouldReuseRoute = () => false;
- this.router.onSameUrlNavigation = 'reload';
- this.router.navigate(['/substances-ssg4m', this.id, 'edit']);
- */
- }
- }, (error: SubstanceFormResults) => {
- this.loadingService.setLoading(false);
- this.isLoading = false;
- // If submit was not successful, display Message
- this.saveDelayedMessage = " Network communication restored, RECORD HAS NOT BEEN SAVED. Please resave the record.";
+ if (this.validationMessages && this.validationMessages.length > 0) {
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ this.validationResult = false;
+ this.showSubmissionMessages = true;
+ return true;
+ }
- this.showSubmissionMessages = true;
- this.submissionMessage = null;
- if (error.validationMessages && error.validationMessages.length) {
- this.validationResult = error.isSuccessfull;
- this.validationMessages = error.validationMessages
- .filter(message => message.messageType.toUpperCase() === 'ERROR' || message.messageType.toUpperCase() === 'WARNING');
- this.showSubmissionMessages = true;
- } else {
- this.submissionMessage = 'There was a problem with your submission';
- this.addServerError(error.serverError);
- setTimeout(() => {
- this.showSubmissionMessages = false;
- this.submissionMessage = null;
- }, 8000);
- }
+ return false;
+ }
+
+ // Processes a single draft from localStorage.
+ private async processSingleDraft(
+ key: string,
+ jsonStr: string,
+ ): Promise {
+ try {
+ const entry = JSON.parse(localStorage.getItem(key));
+ console.log(
+ "Processing draft entry from localStorage key: " +
+ JSON.stringify(entry),
+ );
+
+ if (!entry || !entry.substance) {
+ return;
+ }
+
+ const draftSub = entry.substance;
+
+ if (!this.isDraftReferencedInForm(draftSub, entry, jsonStr)) {
+ return;
+ }
+
+ const hasErrors = await this.validateAndCollectDraftErrors(
+ draftSub,
+ entry,
+ );
+ if (hasErrors) {
+ return;
+ }
+
+ // Clean up temporary fields and save
+ if (draftSub && draftSub.$$tmpStructureId) {
+ delete draftSub.$$tmpStructureId;
+ }
+
+ await this.saveDraftSubstance(draftSub);
+ } catch (e) {
+ this.addServerError(e.serverError);
+ }
+ }
+
+ // Checks if a draft's temporary structure ID is referenced in the current form JSON.
+ private isDraftReferencedInForm(
+ draftSub: any,
+ entry: any,
+ jsonStr: string,
+ ): boolean {
+ const tmpStructureId =
+ draftSub.$$tmpStructureId || entry.$$tmpStructureId || null;
+
+ if (!tmpStructureId) {
+ return false;
+ }
+
+ return jsonStr.indexOf('"' + tmpStructureId + '"') !== -1;
+ }
+
+ // Validates a draft and collects any validation errors/warnings.
+ // returns true if there are validation errors that should skip saving, false otherwise
+ private async validateAndCollectDraftErrors(
+ draftSub: any,
+ entry: any,
+ ): Promise {
+ const validationResult: any = await new Promise((resolve) => {
+ this.substanceService
+ .validateSubstance(draftSub)
+ .pipe(take(1))
+ .subscribe(
+ (res) => resolve(res),
+ (err) => resolve({ error: err }),
+ );
+ });
+
+ if (
+ validationResult &&
+ validationResult.validationMessages &&
+ validationResult.validationMessages.length > 0
+ ) {
+ const filteredValidations = this.filterAndPrefixValidationMessages(
+ validationResult.validationMessages,
+ entry,
+ draftSub,
+ );
+
+ this.validationMessages = [
+ ...this.validationMessages,
+ ...filteredValidations,
+ ];
+
+ return filteredValidations.length > 0;
+ }
+
+ return false;
+ }
+
+ private filterAndPrefixValidationMessages(
+ messages: ValidationMessage[],
+ entry: any,
+ draftSub: any,
+ ): ValidationMessage[] {
+ return messages
+ .filter(
+ (message) =>
+ message.messageType.toUpperCase() === "ERROR" ||
+ message.messageType.toUpperCase() === "WARNING",
+ )
+ .map((msg) => {
+ msg.message =
+ 'Draft "' +
+ (entry.name || draftSub.names?.[0]?.name || entry.key) +
+ '": ' +
+ msg.message;
+ return msg;
});
- this.subscriptions.push(this.submitSubscription);
+ }
- }; //window
+ private async saveDraftSubstance(draftSub: any): Promise {
+ return new Promise((resolve) => {
+ this.substanceService
+ .saveSubstance(draftSub)
+ .pipe(take(1))
+ .subscribe(
+ (resp) => resolve(resp),
+ (err) => resolve({ error: err }),
+ );
+ });
+ }
- let tempCallback = window["schemeUtil"].onFinishedLayout;
- window["schemeUtil"].onFinishedLayout = (s)=>{
- window["schemeUtil"].onFinishedLayout =(ss)=>{};
+ private prepareFinalJson(): string {
+ try {
+ if (this.json) {
+ this.removeTmpStructureIdFields(this.json);
+ return JSON.stringify(this.json);
+ }
+ } catch (e) {
+ // Ignore errors and return current JSON
+ }
+ return JSON.stringify(this.json);
+ }
- setTimeout(tempCallback(s),3000);
- };
-
- window['schemeUtil'].renderScheme(window['schemeUtil'].makeDisplayGraph(JSON.parse(ssgjs)), "#scheme-viz-view");
+ // Saves the SSG4m synthetic pathway to the backend.
+ private savePathway(): void {
+ this.submitSubscription = this.substanceSsg4mService
+ .saveSsg4m(this.ssg4mSyntheticPathway)
+ .pipe(take(1))
+ .subscribe(
+ (response) => this.handleSaveSuccess(response),
+ (error: SubstanceFormResults) => this.handleSaveError(error),
+ );
+ this.subscriptions.push(this.submitSubscription);
+ }
+
+ private handleSaveSuccess(response: any): void {
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ this.validationMessages = null;
+ this.showSubmissionMessages = false;
+ this.validationResult = false;
+
+ const isSuccessful =
+ response &&
+ (response.synthPathwaySkey ||
+ this.configService.configData.isPfdaVersion);
+
+ if (!isSuccessful) {
+ return;
+ }
+
+ if (response.synthPathwaySkey) {
+ this.id = response.synthPathwaySkey.toString();
+ }
+ this.isSavedSuccessful = true;
+
+ // Handle UI messaging based on whether cancel was clicked
+ if (this.isCancelBtnClicked === true && this.isSavedSuccessful === true) {
+ this.saveDelayedMessage =
+ " Network communication restored, click here to refresh with saved version.";
+ } else {
+ this.saveDelayedMessage = "";
+ this.isCancelBtnClicked = false;
+ this.openSuccessDialog(
+ undefined,
+ this.configService.configData.isPfdaVersion ? response.fileUrl : null,
+ );
+ }
+ }
+
+ private handleSaveError(error: SubstanceFormResults): void {
+ this.loadingService.setLoading(false);
+ this.isLoading = false;
+ this.saveDelayedMessage =
+ " Network communication restored, RECORD HAS NOT BEEN SAVED. Please resave the record.";
+
+ this.showSubmissionMessages = true;
+ this.submissionMessage = null;
+
+ if (error.validationMessages && error.validationMessages.length) {
+ this.validationResult = error.isSuccessfull;
+ this.validationMessages = error.validationMessages.filter(
+ (message) =>
+ message.messageType.toUpperCase() === "ERROR" ||
+ message.messageType.toUpperCase() === "WARNING",
+ );
+ this.showSubmissionMessages = true;
+ } else {
+ this.submissionMessage = "There was a problem with your submission";
+ this.addServerError(error.serverError);
+ setTimeout(() => {
+ this.showSubmissionMessages = false;
+ this.submissionMessage = null;
+ }, 8000);
+ }
}
cancelSubmit() {
@@ -1091,18 +1719,20 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.isLoading = false;
this.isCancelBtnClicked = true;
- this.saveDelayedMessage = "Warning: Network traffic is delayed, attempting to reconnect to the server ... please save a local copy of the record.";
+ this.saveDelayedMessage =
+ "Warning: Network traffic is delayed, attempting to reconnect to the server ... please save a local copy of the record.";
}
refreshPage() {
// Refresh the current page, this will not cause record locking issue
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
- this.router.onSameUrlNavigation = 'reload';
- if (this.showHeaderBar && this.showHeaderBar === 'false') {
- const route = '/substances-ssg4m/' + this.id + '/edit?header=' + this.showHeaderBar;
+ this.router.onSameUrlNavigation = "reload";
+ if (this.showHeaderBar && this.showHeaderBar === "false") {
+ const route =
+ "/substances-ssg4m/" + this.id + "/edit?header=" + this.showHeaderBar;
this.router.navigateByUrl(route);
} else {
- this.router.navigate(['/substances-ssg4m', this.id, 'edit']);
+ this.router.navigate(["/substances-ssg4m", this.id, "edit"]);
}
}
@@ -1110,7 +1740,7 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.validationMessages.splice(index, 1);
if (this.validationMessages.length === 0) {
- this.submissionMessage = 'Substance is Valid. Would you like to submit?';
+ this.submissionMessage = "Substance is Valid. Would you like to submit?";
}
}
@@ -1120,19 +1750,22 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
this.validationMessages = null;
const message: ValidationMessage = {
- actionType: 'server failure',
+ actionType: "server failure",
links: [],
appliedChange: false,
suggestedChange: false,
- messageType: 'ERROR',
- message: 'Unknown Server Error'
+ messageType: "ERROR",
+ message: "Unknown Server Error",
};
if (error && error.error && error.error.message) {
- message.message = 'Server Error ' + (error.status + ': ' || ': ') + error.error.message;
- } else if (error && error.error && (typeof error.error) === 'string') {
- message.message = 'Server Error ' + (error.status + ': ' || '') + error.error;
+ message.message =
+ "Server Error " + (error.status + ": " || ": ") + error.error.message;
+ } else if (error && error.error && typeof error.error === "string") {
+ message.message =
+ "Server Error " + (error.status + ": " || "") + error.error;
} else if (error && error.message) {
- message.message = 'Server Error ' + (error.status + ': ' || '') + error.message;
+ message.message =
+ "Server Error " + (error.status + ": " || "") + error.message;
}
this.validationMessages = [message];
this.showSubmissionMessages = true;
@@ -1144,16 +1777,16 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
dismissAllValidationMessages(): void {
for (let i = this.validationMessages.length - 1; i >= 0; i--) {
- if (this.validationMessages[i].messageType !== 'ERROR') {
+ if (this.validationMessages[i].messageType !== "ERROR") {
this.validationMessages.splice(i, 1);
}
}
if (this.validationMessages.length === 0) {
- this.submissionMessage = 'Substance is Valid. Would you like to submit?';
+ this.submissionMessage = "Substance is Valid. Would you like to submit?";
}
}
- @HostListener('window:beforeunload', ['$event'])
+ @HostListener("window:beforeunload", ["$event"])
unloadNotification($event: any) {
if (this.substanceSsg4mService.isSubstanceUpdated) {
$event.returnValue = true;
@@ -1167,8 +1800,20 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
.toString(16)
.substring(1);
}
- return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
- s4() + '-' + s4() + s4() + s4();
+ return (
+ s4() +
+ s4() +
+ "-" +
+ s4() +
+ "-" +
+ s4() +
+ "-" +
+ s4() +
+ "-" +
+ s4() +
+ s4() +
+ s4()
+ );
}
const old = oldraw;
@@ -1208,47 +1853,48 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
const refs = refHolders[i].references;
for (let j = 0; j < refs.length; j++) {
const or = refs[j];
- if (typeof or === 'object') { continue; }
+ if (typeof or === "object") {
+ continue;
+ }
refs[j] = _map[or];
}
}
_.remove(old.codes, {
- codeSystem: 'BDNUM'
+ codeSystem: "BDNUM",
});
const createHolders = jp.query(old, '$..[?(@.created)]');
for (let i = 0; i < createHolders.length; i++) {
const rec = createHolders[i];
- delete rec['created'];
- delete rec['createdBy'];
- delete rec['lastEdited'];
- delete rec['lastEditedBy'];
+ delete rec["created"];
+ delete rec["createdBy"];
+ delete rec["lastEdited"];
+ delete rec["lastEditedBy"];
}
const originHolders = jp.query(old, '$..[?(@.originatorUuid)]');
for (let i = 0; i < originHolders.length; i++) {
const rec = originHolders[i];
- delete rec['originatorUuid'];
+ delete rec["originatorUuid"];
}
delete old.approvalID;
delete old.approved;
delete old.approvedBy;
- old.status = 'pending';
- if ((importType) && (importType === 'definition')) {
+ old.status = "pending";
+ if (importType && importType === "definition") {
old.names = [];
old.codes = [];
old.notes = [];
old.relationships = [];
old.tags = [];
}
- delete old['createdBy'];
- delete old['created'];
- delete old['lastEdited'];
- delete old['lastEditedBy'];
- delete old['version'];
- delete old['$$update'];
- delete old['changeReason'];
-
+ delete old["createdBy"];
+ delete old["created"];
+ delete old["lastEdited"];
+ delete old["lastEditedBy"];
+ delete old["version"];
+ delete old["$$update"];
+ delete old["changeReason"];
if (true) {
const refSet = {};
@@ -1258,7 +1904,9 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
const refs = refHolders2[i].references;
for (let j = 0; j < refs.length; j++) {
const or = refs[j];
- if (typeof or === 'object') { continue; }
+ if (typeof or === "object") {
+ continue;
+ }
refSet[or] = true;
}
}
@@ -1274,71 +1922,93 @@ export class SubstanceSsg4ManufactureFormComponent implements OnInit, AfterViewI
.value();
old.references = nrefs;
-
}
return old;
}
- openSuccessDialog(type?: string): void {
+ openSuccessDialog(type?: string, fileUrl?: string): void {
let data = {
- isCoreSubstance: 'false'
+ isCoreSubstance: "false",
+ type: null,
+ fileUrl: null,
};
- const dialogRef = this.dialog.open(SubmitSuccessDialogComponent, {
- data: data
- });
- this.overlayContainer.style.zIndex = '1002';
- const dialogSubscription = dialogRef.afterClosed().pipe(take(1)).subscribe((response?: 'continue') => {
-
- this.substanceSsg4mService.bypassUpdateCheck();
- if (response === 'continue') {
+ if (this.configService.configData.isPfdaVersion) {
+ data = {
+ isCoreSubstance: "true",
+ type: "submit",
+ fileUrl: fileUrl,
+ };
+ }
- // Refresh the current page, this will not cause record locking issue
- this.router.routeReuseStrategy.shouldReuseRoute = () => false;
- this.router.onSameUrlNavigation = 'reload';
- if (this.showHeaderBar && this.showHeaderBar === 'false') {
- const route = '/substances-ssg4m/' + this.id + '/edit?header=' + this.showHeaderBar;
- this.router.navigateByUrl(route);
- } else {
- this.router.navigate(['/substances-ssg4m', this.id, 'edit']);
- }
+ const dialogRef = this.dialog.open(SubmitSuccessDialogComponent, {
+ data: data,
+ });
+ this.overlayContainer.style.zIndex = "1002";
+
+ const dialogSubscription = dialogRef
+ .afterClosed()
+ .pipe(take(1))
+ .subscribe((response?: "continue") => {
+ this.substanceSsg4mService.bypassUpdateCheck();
+ if (response === "continue") {
+ // Refresh the current page, this will not cause record locking issue
+ this.router.routeReuseStrategy.shouldReuseRoute = () => false;
+ this.router.onSameUrlNavigation = "reload";
+ if (this.showHeaderBar && this.showHeaderBar === "false") {
+ const route =
+ "/substances-ssg4m/" +
+ this.id +
+ "/edit?header=" +
+ this.showHeaderBar;
+ this.router.navigateByUrl(route);
+ } else {
+ this.router.navigate(["/substances-ssg4m", this.id, "edit"]);
+ }
- // } else if (response === 'browse') {
- // this.router.navigate(['/browse-substance']);
- // } else if (response === 'home') {
- // this.router.navigate(['/home']);
- // } else {
- this.showSubmissionMessages = true;
- this.validationResult = false;
- this.submissionMessage = '';
- /*
+ // } else if (response === 'browse') {
+ // this.router.navigate(['/browse-substance']);
+ // } else if (response === 'home') {
+ // this.router.navigate(['/home']);
+ // } else {
+ this.showSubmissionMessages = true;
+ this.validationResult = false;
+ this.submissionMessage = "";
+ /*
setTimeout(() => {
this.showSubmissionMessages = false;
this.submissionMessage = '';
this.router.navigate(['/substances-ssg4m', this.id, 'edit']);
}, 3000);
*/
- }
- });
+ } else if (response === "browse") {
+ this.router.navigate(["/browse-substance"]);
+ } else if (response === "viewInPfda") {
+ // View the submitted substance file in the user's precisionFDA home
+ window.location.assign(fileUrl);
+ }
+ });
this.subscriptions.push(dialogSubscription);
-
}
mergeConcept() {
this.feature = undefined;
const dialogRef = this.dialog.open(MergeConceptDialogComponent, {
- width: '900px', data: { uuid: this.id }
+ width: "900px",
+ data: { uuid: this.id },
});
- this.overlayContainer.style.zIndex = '1002';
+ this.overlayContainer.style.zIndex = "1002";
}
definitionSwitch() {
this.feature = undefined;
const dialogRef = this.dialog.open(DefinitionSwitchDialogComponent, {
- width: '900px', data: { uuid: this.id }, autoFocus: false
+ width: "900px",
+ data: { uuid: this.id },
+ autoFocus: false,
});
- this.overlayContainer.style.zIndex = '1000';
+ this.overlayContainer.style.zIndex = "1000";
}
fixLink(link: string) {
diff --git a/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts b/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts
index 5cc38ad4b..dfbf0df80 100644
--- a/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts
+++ b/src/app/core/substance-ssg4m/substance-ssg4m-form.service.ts
@@ -7,10 +7,11 @@ import { Ssg4mSyntheticPathway, Ssg4mSyntheticPathwayDetail } from './model/subs
import { SubstanceDetail } from '../substance/substance.model';
import { SubstanceName } from '../substance/substance.model';
import { SubstanceFormDefinition, SubunitSequence, ValidationResults, ValidationMessage } from '../substance-form/substance-form.model';
-import { Observable, Subject, ReplaySubject, Subscription } from 'rxjs';
+import { Observable, Subject, ReplaySubject, Subscription, concatMap, throwError } from 'rxjs';
import { SubstanceService } from '@gsrs-core/substance/substance.service';
import { UtilsService } from '@gsrs-core/utils/utils.service';
import { StructureService } from '@gsrs-core/structure';
+import { AuthService } from '@gsrs-core/auth';
@Injectable({
providedIn: 'root'
@@ -55,6 +56,7 @@ export class SubstanceSsg4mService implements OnDestroy {
public utilsService: UtilsService,
private structureService: StructureService,
public http: HttpClient,
+ private authService: AuthService,
public configService: ConfigService
) {
this.substanceEmitter = new ReplaySubject();
@@ -312,7 +314,27 @@ export class SubstanceSsg4mService implements OnDestroy {
const options = {
body: ssg4m
};
- return this.http.request(method, url, options);
+
+ if (!this.configService.configData.isPfdaVersion) {
+ return this.http.request(method, url, options);
+ } else {
+ return this.authService.getAuth().pipe(
+ concatMap(auth =>
+ auth
+ ? this.http.request(method, url, options)
+ : this.authService.pfdaLogin().pipe(
+ concatMap(success =>
+ success
+ ? this.http.request(method, url, options)
+ : throwError(() => ({
+ type: 'AUTH',
+ message: 'Authentication failed',
+ }))
+ )
+ )
+ )
+ );
+ }
}
validateSsg4m(ssg4m: Ssg4mSyntheticPathway): Observable {
diff --git a/src/app/core/substance/substance-image.directive.ts b/src/app/core/substance/substance-image.directive.ts
index 8da808ed4..be521abbf 100644
--- a/src/app/core/substance/substance-image.directive.ts
+++ b/src/app/core/substance/substance-image.directive.ts
@@ -1,11 +1,11 @@
-import { Directive, ElementRef, Input, AfterViewInit } from '@angular/core';
-import { UtilsService } from '../utils/utils.service';
-import { HttpClient } from '@angular/common/http';
-import { ConfigService } from '@gsrs-core/config/config.service';
+import { Directive, ElementRef, Input, AfterViewInit } from "@angular/core";
+import { UtilsService } from "../utils/utils.service";
+import { HttpClient } from "@angular/common/http";
+import { ConfigService } from "@gsrs-core/config/config.service";
@Directive({
- selector: '[appSubstanceImage]',
- standalone: false
+ selector: "[appSubstanceImage]",
+ standalone: false,
})
export class SubstanceImageDirective implements AfterViewInit {
private privateEntityId: string;
@@ -21,13 +21,16 @@ export class SubstanceImageDirective implements AfterViewInit {
private el: ElementRef,
private utilsService: UtilsService,
private configService: ConfigService,
- private http: HttpClient
+ private http: HttpClient,
) {
this.imageElement = this.el.nativeElement as HTMLImageElement;
}
ngAfterViewInit() {
this.isAfterViewInit = true;
+ this.imageElement.onerror = () => {
+ this.setNoImage();
+ };
this.setImageSrc();
}
@@ -80,12 +83,20 @@ export class SubstanceImageDirective implements AfterViewInit {
}
private setImageSrc(): void {
- const useDataUrlConfig = this.configService.configData && this.configService.configData.useDataUrl || false;
+ const useDataUrlConfig =
+ (this.configService.configData &&
+ this.configService.configData.useDataUrl) ||
+ false;
if (this.isAfterViewInit) {
if (this.privateEntityId) {
if (this.privateVersion) {
const srcUrl = this.utilsService.getStructureImgUrl(
- this.privateEntityId, this.privateSize, this.privateStereo, this.privateAtomMaps, this.privateVersion);
+ this.privateEntityId,
+ this.privateSize,
+ this.privateStereo,
+ this.privateAtomMaps,
+ this.privateVersion,
+ );
if (useDataUrlConfig === true) {
this.setImageSrcAsBlob(srcUrl);
} else {
@@ -93,55 +104,85 @@ export class SubstanceImageDirective implements AfterViewInit {
}
} else {
let srcUrl = this.utilsService.getStructureImgUrl(
- this.privateEntityId, this.privateSize, this.privateStereo, this.privateAtomMaps);
+ this.privateEntityId,
+ this.privateSize,
+ this.privateStereo,
+ this.privateAtomMaps,
+ );
if (this.privateAltId) {
srcUrl = this.utilsService.getStructureImgUrl(
- this.privateEntityId, this.privateSize, this.privateStereo, this.privateAtomMaps, null, this.privateAltId);
+ this.privateEntityId,
+ this.privateSize,
+ this.privateStereo,
+ this.privateAtomMaps,
+ null,
+ this.privateAltId,
+ );
} else {
}
-
+
if (useDataUrlConfig === true) {
this.setImageSrcAsBlob(srcUrl);
} else {
this.imageElement.src = srcUrl;
}
}
- } else {
- let srcUrl =`${this.configService.environment.baseHref || ''}assets/images/noimage.svg`;
-
- if(this.privateAltId) {
- srcUrl = this.utilsService.getStructureImgUrl(
- this.privateEntityId, this.privateSize, this.privateStereo, this.privateAtomMaps, null, this.privateAltId);
- }
- if(this.privateSize){
- this.imageElement.height = this.privateSize;
- this.imageElement.width = this.privateSize;
- }else{
- this.imageElement.height = 150;
- this.imageElement.width = 150;
- }
- if (useDataUrlConfig === true) {
- this.setImageSrcAsBlob(srcUrl);
} else {
- this.imageElement.src = srcUrl;
+ if (this.privateAltId) {
+ const srcUrl = this.utilsService.getStructureImgUrl(
+ this.privateEntityId,
+ this.privateSize,
+ this.privateStereo,
+ this.privateAtomMaps,
+ null,
+ this.privateAltId,
+ );
+ if (useDataUrlConfig === true) {
+ this.setImageSrcAsBlob(srcUrl);
+ } else {
+ this.imageElement.src = srcUrl;
+ }
+ } else {
+ this.setNoImage();
+ }
}
-
+ this.imageElement.alt = "structure image";
}
- this.imageElement.alt = 'structure image';
+ }
+
+ private setNoImage(): void {
+ const noImageUrl = `${this.configService.environment.baseHref || ""}assets/images/noimage.svg`;
+ // Temporarily remove onerror to avoid infinite loop if noimage.svg itself fails
+ this.imageElement.onerror = null;
+ this.imageElement.src = noImageUrl;
+ if (this.privateSize) {
+ this.imageElement.height = this.privateSize;
+ this.imageElement.width = this.privateSize;
+ } else {
+ this.imageElement.height = 150;
+ this.imageElement.width = 150;
}
}
private setImageSrcAsBlob(srcUrl: any): void {
// Getting Image Source as Blob
- this.http.get(srcUrl, { responseType: "blob" }).subscribe(imgDat => {
- let reader = new FileReader();
- reader.addEventListener("load", () => {
- this.imageElement.src = reader.result.toString();
- }, false);
- if (imgDat) {
- reader.readAsDataURL(imgDat);
- }
- });
+ this.http.get(srcUrl, { responseType: "blob" }).subscribe(
+ (imgDat) => {
+ let reader = new FileReader();
+ reader.addEventListener(
+ "load",
+ () => {
+ this.imageElement.src = reader.result.toString();
+ },
+ false,
+ );
+ if (imgDat) {
+ reader.readAsDataURL(imgDat);
+ }
+ },
+ () => {
+ this.setNoImage();
+ },
+ );
}
-
}
diff --git a/src/app/core/substance/substance.service.ts b/src/app/core/substance/substance.service.ts
index bea4960ad..c0340a270 100644
--- a/src/app/core/substance/substance.service.ts
+++ b/src/app/core/substance/substance.service.ts
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams, HttpClientJsonpModule, HttpParameterCodec } from '@angular/common/http';
-import { BehaviorSubject, interval, Observable, Observer, Subject, throwError } from 'rxjs';
+import { BehaviorSubject, concatMap, filter, interval, Observable, Observer, Subject, throwError } from 'rxjs';
import { ConfigService } from '../config/config.service';
import { BaseHttpService } from '../base/base-http.service';
import {
@@ -26,6 +26,8 @@ import { StructuralUnit } from '@gsrs-core/substance';
import {HierarchyNode} from '@gsrs-core/substances-browse/substance-hierarchy/hierarchy.model';
import { SubstanceDependenciesImageNode } from '@gsrs-core/substance-details/substance-dependencies-image/substance-dependencies-image.model';
+import { stringify } from 'querystring';
+import { AuthService } from "@gsrs-core/auth";
class CustomEncoder implements HttpParameterCodec {
encodeKey(key: string): string {
return encodeURIComponent(key);
@@ -57,6 +59,7 @@ export class SubstanceService extends BaseHttpService {
tempObject: any;
constructor(
public http: HttpClient,
+ private authService: AuthService,
public configService: ConfigService,
private sanitizer: DomSanitizer,
private utilsService: UtilsService,
@@ -879,15 +882,35 @@ export class SubstanceService extends BaseHttpService {
}
saveSubstance(substance: SubstanceDetail, type?: string): Observable {
+ // Remove temporary structure ID if present (created when loading draft into G4SSM form)
+ if (substance && (substance as any).$$tmpStructureId) {
+ delete (substance as any).$$tmpStructureId;
+ }
+
+ const method = type === 'import' || !substance.uuid ? 'POST' : 'PUT';
+ const options = { body: substance };
+
const url = `${this.apiBaseUrl}substances?view=internal`;
- let method = substance.uuid ? 'PUT' : 'POST';
- if (type && type === 'import') {
- method = 'POST';
+ if (!this.configService.configData.isPfdaVersion) {
+ return this.http.request(method, url, options);
+ } else {
+ return this.authService.getAuth().pipe(
+ concatMap(auth =>
+ auth
+ ? this.http.request(method, url, options)
+ : this.authService.pfdaLogin().pipe(
+ concatMap(success =>
+ success
+ ? this.http.request(method, url, options)
+ : throwError(() => ({
+ type: 'AUTH',
+ message: 'Authentication failed',
+ }))
+ )
+ )
+ )
+ );
}
- const options = {
- body: substance
- };
- return this.http.request(method, url, options);
}
saveSubstanceWithoutValidation(substance: SubstanceDetail, type?: string): Observable {
diff --git a/src/app/core/substances-browse/substances-browse.component.ts b/src/app/core/substances-browse/substances-browse.component.ts
index 54167b604..5b4d3218b 100644
--- a/src/app/core/substances-browse/substances-browse.component.ts
+++ b/src/app/core/substances-browse/substances-browse.component.ts
@@ -264,7 +264,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr
this.smiles = this.activatedRoute.snapshot.queryParams['smiles'] || '';
// the sort order should be set to default (similarity) for structure searches, last edited for all others
this.order = this.activatedRoute.snapshot.queryParams['order'] ||
- (this.privateStructureSearchTerm && this.privateStructureSearchTerm !== '' ? 'default' : '$root_lastEdited');
+ (this.privateStructureSearchTerm && this.privateStructureSearchTerm !== '' ? 'default' : '$root_lastEdited');
this.view = this.activatedRoute.snapshot.queryParams['view'] || 'cards';
this.pageSize = parseInt(this.activatedRoute.snapshot.queryParams['pageSize'], null) || 10;
const deprecated = this.activatedRoute.snapshot.queryParams['showDeprecated'];
@@ -1498,7 +1498,7 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr
} else {
this.idLists = [];
}
- } // pagingResponse
+ } // pagingResponse
}, error => {
console.log('Error during search substance');
}, () => {
@@ -1564,4 +1564,4 @@ export class SubstancesBrowseComponent implements OnInit, AfterViewInit, OnDestr
};
}
-}
\ No newline at end of file
+}
diff --git a/src/app/fda/fda.module.ts b/src/app/fda/fda.module.ts
index 43c7178af..660034a0d 100644
--- a/src/app/fda/fda.module.ts
+++ b/src/app/fda/fda.module.ts
@@ -27,7 +27,6 @@ import { SubstanceApplicationMatchListComponent} from './substance-browse/substa
import { ApplicationsBrowseComponent } from './application/applications-browse/applications-browse.component';
import { ClinicalTrialsBrowseComponent } from './clinical-trials/clinical-trials-browse/clinical-trials-browse.component';
import { fdaSubstanceCardsFilters } from './substance-details/fda-substance-cards-filters.constant';
-import { SsoRefreshService } from './service/sso-refresh.service';
import { ProductService } from './product/service/product.service';
import { GeneralService} from './service/general.service';
import { ShowApplicationToggleComponent } from './substance-browse/show-application-toggle/show-application-toggle.component';
@@ -59,12 +58,6 @@ const fdaRoutes: Routes = [
}
];
-export function init_sso_refresh_service(ssoService: SsoRefreshService) {
- return() => {
- ssoService.init();
- };
-}
-
@NgModule({
imports: [
CommonModule,
@@ -100,15 +93,6 @@ export function init_sso_refresh_service(ssoService: SsoRefreshService) {
ShowApplicationToggleComponent
],
exports: [],
- providers: [
- SsoRefreshService,
- {
- provide: APP_INITIALIZER,
- useFactory: init_sso_refresh_service,
- deps: [SsoRefreshService],
- multi: true
- }
- ]
})
export class FdaModule {
constructor(
diff --git a/src/styles/_material-overrides.scss b/src/styles/_material-overrides.scss
index ce80c078d..7e790cd44 100644
--- a/src/styles/_material-overrides.scss
+++ b/src/styles/_material-overrides.scss
@@ -1083,9 +1083,10 @@ app-loading .mat-mdc-progress-spinner {
// ============================================================================
// Tablet/Mobile: Hide most navigation buttons at 1350px, show only Logo, Menu, Search, and Login
+// Note: scoped to :not(.pfda-toolbar) to avoid hiding pfda-toolbar buttons
@media (max-width: 1350px) {
- .mat-toolbar,
- .mat-mdc-toolbar {
+ .mat-toolbar:not(.pfda-toolbar),
+ .mat-mdc-toolbar:not(.pfda-toolbar) {
// Logo container - always visible
> .logo-container {
display: flex !important;