Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/core/assets/data/application_dictionary.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
"type":"string",
"cvDomain":null,
"priority":"x",
"suggest":null
"suggest":"Substance_Key"
},
"Title":{
"lucenePath":"root_title",
Expand Down
8 changes: 7 additions & 1 deletion src/app/core/facets-manager/facet-display.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,16 @@ export class FacetDisplayPipe implements PipeTransform {
return 'Submit Date';
}
if (name === 'root_creationDate') {
return 'Record Create Date';
return 'Record Created Date';
}
if (name === 'root_createdDate') {
return 'Record Created Date';
}
if (name === 'root_lastModifiedDate') {
return 'Record Last Edited';
}
if (name === 'root_modifiedDate') {
return 'Record Last Edited';
}

return name.trim();
Expand Down
7 changes: 4 additions & 3 deletions src/app/fda/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,7 @@
"Product Name",
"Dosage Form Name",
"Substance UUID",
"Substance Class",
"Ingredient Name (Preferred)",
"Ingredient Name",
"Has Ingredients",
Expand Down Expand Up @@ -1183,10 +1184,10 @@
"Sponsor Report Submitter Name"
],
"admin": [
"root_createdDate",
"Record Created By",
"Record Last Edited By",
"Record Create Date",
"Record Last Edited"
"root_modifiedDate",
"Record Last Edited By"
]
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy

newAssaySetObject: InvitroAssaySet;
newAssaySet: string;
correctTargetNameApprovalID: string;
correctLigandApprovalID: string;

checkBoxAssaySetList: Array<any> = [];
existingAssaySetList: Array<any> = [];
Expand Down Expand Up @@ -154,13 +156,13 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy
} // if response
} // if record has JSON
else {
// if No JSON file selected, show message to user
// Initialized the Assay Objects
this.invitroPharmacologyService.loadAssayOnly();
this.assay = this.invitroPharmacologyService.assay;
// if No JSON file selected, show message to user
// Initialized the Assay Objects
this.invitroPharmacologyService.loadAssayOnly();
this.assay = this.invitroPharmacologyService.assay;

// Get All the Assay Sets for checkboxes on the form
this.getAllAssaySets();
// Get All the Assay Sets for checkboxes on the form
this.getAllAssaySets();

// Stop the Loading/Spinner after the form data is loaded
this.isLoading = false;
Expand Down Expand Up @@ -423,6 +425,24 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy
}
}

if ((this.assay.targetName) && (this.assay.targetNameApprovalId)) {
if (this.correctTargetNameApprovalID) {
// if NOT SAME, display error message
if (this.correctTargetNameApprovalID !== this.assay.targetNameApprovalId) {
this.setValidationMessage("The Target Name Approval ID for Target Name '" + this.assay.targetName + "' should be '" + this.correctTargetNameApprovalID + "'");
}
}
}

if ((this.assay.ligandSubstrate) && (this.assay.ligandSubstrateApprovalId)) {
if (this.correctLigandApprovalID) {
// if NOT SAME, display error message
if (this.correctLigandApprovalID !== this.assay.ligandSubstrateApprovalId) {
this.setValidationMessage("The Ligand/Substrate Approval ID for Ligand/Substrate '" + this.assay.ligandSubstrate + "' should be '" + this.correctLigandApprovalID + "'");
}
}
}

if (this.validationMessages.length > 0) {
this.showSubmissionMessages = true;
this.loadingService.setLoading(false);
Expand Down Expand Up @@ -569,7 +589,7 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy
const date = new Date();
let jsonFilename = 'invitro_pharm_assay_' + moment(date).format('MMM-DD-YYYY_H-mm-ss');

let data = {jsonData: this.invitroPharmacologyService.assay, jsonFilename: jsonFilename};
let data = { jsonData: this.invitroPharmacologyService.assay, jsonFilename: jsonFilename };

const dialogRef = this.dialog.open(JsonDialogFdaComponent, {
width: '90%',
Expand Down Expand Up @@ -707,6 +727,10 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy
this.assay.targetNameSubstanceKey = substanceKey;
this.assay.targetNameSubstanceKeyType = this.substanceKeyTypeForInvitroPharmacologyConfig;

// Need this for validation, if user changes the Target Name Approval ID in the texbox,
// need to verify that it matches with this Approval ID.
this.correctTargetNameApprovalID = substance.approvalID;

} else if (fieldName === this.HUMAN_HOMOLOG_TARGET) {
this.assay.humanHomologTargetApprovalId = substance.approvalID;
this.assay.humanHomologTargetSubstanceKey = substanceKey;
Expand All @@ -717,6 +741,10 @@ export class InvitroPharmacologyAssayFormComponent implements OnInit, OnDestroy
this.assay.ligandSubstrateSubstanceKey = substanceKey;
this.assay.ligandSubstrateSubstanceKeyType = this.substanceKeyTypeForInvitroPharmacologyConfig;

// Need this for validation, if user changes the Ligand/Substrate Approval ID in the texbox,
// need to verify that it matches with this Approval ID.
this.correctLigandApprovalID = substance.approvalID;

} else if (fieldName === this.ANALYTE) {
this.assay.invitroAssayAnalytes[indexRow].analyteSubstanceKey = substanceKey;
this.assay.invitroAssayAnalytes[indexRow].analyteSubstanceKeyType = this.substanceKeyTypeForInvitroPharmacologyConfig;
Expand Down
1 change: 1 addition & 0 deletions src/app/fda/product/model/product.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export interface ProductIngredient {
_basisOfStrengthSubstanceUuid?: string;
_basisOfStrengthIngredientName?: string;
_basisOfStrengthActiveMoieties?: Array<String>;
_substanceClass?: string;
$$ingredientNameValidation?: string;
$$basisOfStrengthValidation?: string;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,14 @@ export class ProductDetailsBaseComponent implements OnInit, AfterViewInit, OnDes

} // if Substance is public

// Substance Type, convert from 'Chemical' to 'C', and for other types also
if (response.substanceClass) {
let subClassIndex = this.generalService.substanceClassArray.findIndex(subClass => subClass.class === response.substanceClass);
if (subClassIndex > -1) {
elementIngred._substanceClass = this.generalService.substanceClassArray[subClassIndex].shortDisplay;
}
}

// if Ingredient Type exists
if (elementIngred.ingredientType) {

Expand Down Expand Up @@ -305,6 +313,16 @@ export class ProductDetailsBaseComponent implements OnInit, AfterViewInit, OnDes
}
}

getSubstanceClass(substanceClass: string): string {
if (substanceClass) {
let subClassIndex = this.generalService.substanceClassArray.findIndex(subClass => subClass.shortDisplay === substanceClass);
if (subClassIndex > -1) {
return "The Substance Type is " + this.generalService.substanceClassArray[subClassIndex].longDisplay;
}
}
return "";
}

sortProductCodes() {
this.product.productProvenances.forEach((prov, indexProv) => {
// Sort by Product Code Type, such as NDC CODE
Expand Down Expand Up @@ -576,6 +594,13 @@ export class ProductDetailsBaseComponent implements OnInit, AfterViewInit, OnDes
}
}

const ingredSubstanceClassHolders = jp.query(old, '$..[?(@._substanceClass)]');
for (let i = 0; i < ingredSubstanceClassHolders.length; i++) {
if (ingredSubstanceClassHolders[i]._substanceClass) {
delete ingredSubstanceClassHolders[i]._substanceClass;
}
}

delete old['_activeIngredients'];
delete old['_otherIngredients'];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
Product Code:
</div>
<div class="row-property-value-2">

<span *ngFor="let prodCode of prodProv.productCodes; let i = index">
<span *ngIf="i > 0"><br></span>
<b>{{prodCode.productCodeType}}</b>:
Expand Down Expand Up @@ -322,6 +322,17 @@
{{prodIngred._ingredientName}}
</ng-template>

<!-- Show Substance Class/Substance Type -->
<div *ngIf="prodIngred._substanceClass">
&nbsp;&nbsp;
<span class="colororange">
<span
[title]="getSubstanceClass(prodIngred._substanceClass)"><b>{{prodIngred._substanceClass}}</b>
</span>
</span>
</div>

<!-- Show Structure -->
<div class="margintopneg15 marginleft10px" *ngIf="prodIngred._substanceUuid">
<button mat-icon-button color="primary" style="padding:0px;margin:0px;"
(click)="openImageModal(prodIngred._substanceUuid, prodIngred._ingredientName, prodIngred._approvalId)">
Expand Down Expand Up @@ -362,19 +373,6 @@
-->
</div>

<!-- Original Denominator -->
<!--
<div class="marginleft5px">
<span *ngIf="prodIngred.originalDenominatorNumber">
<span class="font11px">&nbsp;Denominator:
<b>{{ prodIngred.originalDenominatorNumber }}&nbsp;{{
prodIngred.originalDenominatorUnit
}}
</b>
</span>
</span>
</div>
-->
</div> <!-- divflex -->

<!-- Display Ingredient Type -->
Expand Down Expand Up @@ -438,6 +436,17 @@
{{prodIngred._ingredientName}}
</ng-template>

<!-- Show Substance Class/Substance Type -->
<div *ngIf="prodIngred._substanceClass">
&nbsp;&nbsp;
<span class="colororange">
<span
[title]="getSubstanceClass(prodIngred._substanceClass)"><b>{{prodIngred._substanceClass}}</b>
</span>
</span>
</div>

<!-- Show Structure -->
<div class="margintopneg15 marginleft10px" *ngIf="prodIngred._substanceUuid">
<button mat-icon-button color="primary" style="padding:0px;margin:0px;"
(click)="openImageModal(prodIngred._substanceUuid, prodIngred._ingredientName, prodIngred._approvalId)">
Expand Down
93 changes: 85 additions & 8 deletions src/app/fda/product/product-form/product-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class ProductFormComponent implements OnInit, AfterViewInit, OnDestroy {

/* Array data type */
private subscriptions: Array<Subscription> = [];

validationMessages: Array<ValidationMessage> = [];
provenanceFieldMessage: Array<String> = [];
effectiveTimeMessage: any[][] = [];
Expand Down Expand Up @@ -128,7 +128,7 @@ export class ProductFormComponent implements OnInit, AfterViewInit, OnDestroy {
this.product.productProvenances = [];
}
this.loadDateFields();

this.loadingService.setLoading(false);
this.isLoading = false;
}
Expand Down Expand Up @@ -315,29 +315,44 @@ export class ProductFormComponent implements OnInit, AfterViewInit, OnDestroy {
this.setValidationMessage(this.manufactureDateMessage);
}

// Validate Product Name and Product Name Type in Product Name section
this.validateProductName();

// Validate Product Code and Product Code Type in Product Code section
this.validateProductCode();

// Validate Ingredient Average, which should be integer/number
if (this.product != null) {
this.product.productManufactureItems.forEach(elementComp => {
this.product.productManufactureItems.forEach((elementComp, indexComp) => {
if (elementComp != null) {
elementComp.productLots.forEach(elementLot => {
elementComp.productLots.forEach((elementLot, indexLot) => {
if (elementLot != null) {

// Validate Ingredient Average, Low, High, LowLimit, HighLimit should be integer/number
elementLot.productIngredients.forEach(elementIngred => {
elementLot.productIngredients.forEach((elementIngred, indexIngred) => {
if (elementIngred != null) {

if (!elementIngred.substanceKey) {
this.setValidationMessage('Ingredient Name is required in Manufacture Item Details ' + (indexComp + 1) + ' in Lot Details ' + (indexLot + 1) + ' in Ingredient Details ' + (indexIngred + 1));
}

if (!elementIngred.ingredientType) {
this.setValidationMessage('Ingredient Type is required in Manufacture Item Details ' + (indexComp + 1) + ' in Lot Details ' + (indexLot + 1) + ' in Ingredient Details ' + (indexIngred + 1));
}

if (elementIngred.average) {
if (this.isNumber(elementIngred.average) === false) {
this.setValidationMessage('Average must be a number');
this.setValidationMessage('Average must be a number in Ingredient Details ' + (indexIngred + 1));
}
}
if (elementIngred.low) {
if (this.isNumber(elementIngred.low) === false) {
this.setValidationMessage('Low must be a number');
this.setValidationMessage('Low must be a number in Ingredient Details ' + (indexIngred + 1));
}
}
if (elementIngred.high) {
if (this.isNumber(elementIngred.high) === false) {
this.setValidationMessage('High must be a number');
this.setValidationMessage('High must be a number in Ingredient Details ' + (indexIngred + 1));
}
}
// Ingredient Name Validation
Expand Down Expand Up @@ -375,6 +390,68 @@ export class ProductFormComponent implements OnInit, AfterViewInit, OnDestroy {
this.validateProvenanceField();
}

validateProductName() {
// Validate in Product Name section. If user enters value in 'Product Name' field and NOT in 'Product Name Type',
// display error message. Same for vice versa.
if (this.product != null) {
if (this.product.productProvenances) {
if (this.product.productProvenances.length > 0) {
this.product.productProvenances.forEach((elementProv, index) => {
if (elementProv != null) {
if (elementProv.productNames) {
if (elementProv.productNames.length > 0) {
elementProv.productNames.forEach((elementName, indexName) => {
if (elementName) {
// If entered value in 'Product Name' field and NOT in 'Product Name Type'.
if (elementName.productName && !elementName.productNameType) {
this.setValidationMessage('Enter value in Product Name Type in Product Provenance ' + (index + 1) + ' in Product Name ' + (indexName + 1));
}
// If entered value in 'Product Name Type' field and NOT in 'Product Name'.
else if (!elementName.productName && elementName.productNameType) {
this.setValidationMessage('Enter value in Product Name in Product Provenance ' + (index + 1) + ' in Product Name ' + (indexName + 1));
}
}
}); // Product Codes loop
}
}
}
});
}
}
}
}

validateProductCode() {
// Validate in Product Code section. If user enters value in 'Product Code' field and NOT in 'Product Code Type',
// display error message. Same for vice versa.
if (this.product != null) {
if (this.product.productProvenances) {
if (this.product.productProvenances.length > 0) {
this.product.productProvenances.forEach((elementProv, index) => {
if (elementProv != null) {
if (elementProv.productCodes) {
if (elementProv.productCodes.length > 0) {
elementProv.productCodes.forEach((elementCode, indexCode) => {
if (elementCode) {
// If entered value in 'Product Code' field and NOT in 'Product Code Type'.
if (elementCode.productCode && !elementCode.productCodeType) {
this.setValidationMessage('Enter value in Product Code Type in Product Provenance ' + (index + 1) + ' in Product Code ' + (indexCode + 1));
}
// If entered value in 'Product Code Type' field and NOT in 'Product Code'.
else if (!elementCode.productCode && elementCode.productCodeType) {
this.setValidationMessage('Enter value in Product Code in Product Provenance ' + (index + 1) + ' in Product Code ' + (indexCode + 1));
}
}
}); // Product Codes loop
}
}
}
});
}
}
}
}

validateProvenanceField(type?: string) {
// Validate Provenance (required field) in Provenance section
if (this.product != null) {
Expand Down
Loading
Loading