import { OnInit, Component, OnDestroy, Inject } from '@angular/core';
import { SearchFactory, SearchService, SimpleSearchResult, HttpParams } from 'skf-search-angular-service';
import { UtilService } from 'src/app/core/services/util-service/util.service';
import { DOCUMENT } from '@angular/common';
import { ComponentLinkService } from 'src/app/core/services/component-link-service/component-link.service';
import { DataResolverService } from 'src/app/core/services/data-resolver/data-resolver.service';
import { PublicationService } from 'src/app/core/services/publication-service/publication.service';
import { ConstantsService } from 'src/app/shared/constants/constants.service';
import { RowResolverService } from 'src/app/core/services/row-resolver.service';
import { AppConfig } from '../../app.config';
import { LabelService } from 'src/app/core/services/label-service/label-service.service';
import { PdfDownloadService } from 'src/app/core/services/pdf-download-service/pdf-download.service';
import { LoadingIndicatorService } from 'src/app/core/services/loading-indicator.service';
import { ActivatedRoute } from '@angular/router';
interface AutomaticButton {
labelText: string;
onClick: () => void;
}
@Component({
selector: 'div[product-detail-page]',
templateUrl: './product-detail-page.component.html',
styleUrls: ['./product-detail-page.component.scss']
})
export class ProductDetailPageComponent implements OnInit, OnDestroy {
opcSearcher: SearchService;
imageUrls = [];
opcData;
opcDoc;
tridionPageData = [];
tridionData;
automaticButtons: AutomaticButton[];
page;
navigation;
subscriptions = [];
shortcuts = {
ShortcutLink: []
};
links = [];
information;
contentForPDPs = [];
noProductFound: boolean;
designation: string;
cadAvailable = true;
// Label
cadDownloadButtonTitle: string;
techSpecLabel: string;
whereToBuyLabel: string;
exportPDFLabel: string;
cadDownloadLabel: string;
constructor(
private searchFactory: SearchFactory,
private util: UtilService,
private linkService: ComponentLinkService,
private dataService: DataResolverService,
private pubService: PublicationService,
public constants: ConstantsService,
private rowResolver: RowResolverService,
private labelService: LabelService,
private pdfService: PdfDownloadService,
private loadingService: LoadingIndicatorService,
private activatedRoute: ActivatedRoute,
@Inject(DOCUMENT) private doc
) {
this.opcSearcher = this.searchFactory.get(`opc-${AppConfig.settings.searchEnvironment.name}`);
}
ngOnInit(): void {
this.labelService.getLabel('cadDownloadButtonTitle').then(label => this.cadDownloadButtonTitle = label);
this.labelService.getLabel('technicalSpecificationOnPDPs').then(label => {
this.techSpecLabel = label;
this.addShortcut({
Title: label,
LinkedComponentId: 'spec'
}, 0);
});
this.labelService.getLabel('prodSpecWhereToBuyButton').then(label => this.whereToBuyLabel = label);
this.labelService.getLabel('prodSpecExportPDFbutton').then(label => this.exportPDFLabel = label);
this.labelService.getLabel('prodSpecCadDownloadButton').then(label => this.cadDownloadLabel = label);
this.resultSubscription();
this.getPageData();
// Fetch navigation data for top menu and breadcrumb
this.dataService.getNavigationData().then(nav => this.navigation = nav);
}
ngOnDestroy(): void {
this.subscriptions.forEach(sub => {
if (sub) {
sub.unsubscribe();
}
});
}
/** Subscribe to search result */
private resultSubscription(): void {
/** Results from search are received here */
this.subscriptions.push(this.opcSearcher.result.subscribe(res => {
this.loadingService.loadingDone();
const result = res as SimpleSearchResult;
const documents = this.util.extract(result, 'documentList', 'documents') || [];
if (documents.length > 0) {
this.opcData = result;
this.opcDoc = documents[0];
this.imageUrls.push(this.opcDoc ? this.opcDoc['image_url'] : 'v2/assets/img/no-image.png');
/**
* Need to pass tabled Id since multiple product specs could have
* been placed on the parent page and we need to be able to find the correct product spec component
*/
const tableId = this.opcDoc.structure_group_id;
this.findTridionContent(tableId);
}
// Variable for showing 'no results' message.
this.noProductFound = documents.length <= 0;
}));
}
/** Fetch data from parent page */
private getPageData(): void {
this.loadingService.startLoading();
const url = this.activatedRoute.snapshot.url;
// Get url to fetch tridion content from parent page
const tridionUrl = url.splice(0, url.length - 1);
const path = tridionUrl.map(segment => segment.path).join('/');
this.dataService.getPageData(`/${path}`).then(res => {
this.page = res;
const mainReg = res['Regions'].find(region => region.Name === 'main');
this.tridionPageData = mainReg['Entities'];
this.getProductData();
});
}
/** Extract correct product spec based on tableId and setup the tridion content for the pdp */
private findTridionContent(tableId: string): void {
this.tridionPageData.forEach(element => {
const MvcData = element['MvcData'];
if (MvcData['ViewName'] === 'ProductSpecMain' && element['ProductTableID'] === tableId) {
this.tridionData = element;
}
// Product spec could be nested inside a tab
if (MvcData['ViewName'] === 'TabMain') {
const allTabs = this.util.extract(element, 'Entities');
const allTabedContent = allTabs.map(tab => this.util.extract(tab, 'TabContnetEntities')).flat();
const productSpec = allTabedContent.find(
entity => this.util.extract(entity, 'MvcData', 'ViewName') === 'ProductSpecMain'
&& this.util.extract(entity, 'ProductTableID') === tableId);
// If this is not undefined means we found a product spec that matches the desired tableID
if (productSpec) {
this.tridionData = productSpec;
}
}
});
const contentList = this.util.extract(this.tridionData, 'ContentForPDPs');
this.information = this.util.extract(this.tridionData, 'Information');
this.automaticButtons = this.resolveAutomaticLinks(this.util.extract(this.tridionData, 'AutoLinks') || []);
if (contentList) {
// Remove shortcuts from the contentForPDPs and add it to the shortcuts
const contentForPDPsTemp = [];
contentList.forEach(element => {
const MvcData = element['MvcData'];
if (MvcData['ViewName'] === 'ShortcutsMain') {
this.addShortcut(element['ShortcutLink'], 2);
} else {
contentForPDPsTemp.push(element);
}
});
this.contentForPDPs = this.rowResolver.transform(contentForPDPsTemp, 'MAIN');
}
const linkTargets = this.util.extract(this.tridionData, 'LinkTarget') || [];
this.links = linkTargets;
linkTargets.forEach(link => {
link['title'] = this.linkService.getTitle(link);
});
}
private resolveAutomaticLinks(automaticLinks): AutomaticButton[] {
// If the editors have included the 'None' value no automatic links should be added
if (automaticLinks.includes('None')) {
return [];
}
const autoLinks = [];
// Functionallity for the where to buy button is not yet specified
const whereToBuyOnClick: () => void = () => {
};
const exportPDFOnClick: () => void = () => {
this.pdfService.download();
};
// Functionallity for the cad download button is not yet specified
const cadDownloadOnClick: () => void = () => {
};
const whereToBuy: AutomaticButton = {
labelText: this.whereToBuyLabel,
onClick: whereToBuyOnClick
};
const exportPDF: AutomaticButton = {
labelText: this.exportPDFLabel,
onClick: exportPDFOnClick
};
const cadDownload: AutomaticButton = {
labelText: this.cadDownloadLabel,
onClick: cadDownloadOnClick
};
// Order here is important so that the where to buy is the leftmost button, CAD download is the second and export pdf the last
if (automaticLinks.includes('Where to buy')) {
autoLinks.push(whereToBuy);
}
if (automaticLinks.includes('CAD download')) {
autoLinks.push(cadDownload);
}
if (automaticLinks.includes('Export to PDF')) {
autoLinks.push(exportPDF);
}
return autoLinks;
}
/** Do a search with designation from URL */
private getProductData(): void {
const url = this.activatedRoute.snapshot.url;
this.designation = url[url.length - 1].path.replace('productid-', '');
const language = this.pubService.getLanguage();
const pubId = this.pubService.getPublicationId();
const system = this.activatedRoute.snapshot.queryParams['system'] || 'metric';
const opcString = `designation=${this.designation}&searcher=details&language=${language}&site=${pubId}&system=${system}`;
this.opcSearcher.doSearchWithParameters({
params: new HttpParams({ 'fromString': opcString }),
});
}
/** Scroll to CAD download component. */
public goToCadDownload() {
const cadComponent = this.doc.getElementById(this.constants.COMPONENT_ID_PREFIX + '-cad-download');
if (cadComponent) {
cadComponent.scrollIntoView(true);
}
}
/** Set CAD download variables */
public setCadDownloadVariables(available) {
this.cadAvailable = available;
if (this.cadAvailable) {
this.addShortcut({
Title: this.cadDownloadLabel,
LinkedComponentId: 'cad-download'
}, 1);
}
}
private addShortcut(shortcuts, position): void {
this.shortcuts['ShortcutLink'].splice(position, 0, ...shortcuts);
this.shortcuts = {
'ShortcutLink': [
...this.shortcuts['ShortcutLink']
]
};
}
}
<div class="pdp" *ngIf="opcDoc">
<div class="product-info">
<div class="container">
<div class="row">
<div class="col-md-5 image-container">
<div class="prod-info">
<h1
[ngClass]="{'title': true, 'title--blue': opcDoc.explorer, 'title--triangle': opcDoc.popular_item }">
{{opcDoc.title}}</h1>
<div *ngIf="opcDoc.popular_item" class="attribute">{{opcDoc.popular_item_label}}</div>
<div *ngIf="opcDoc.explorer" class="attribute">{{opcDoc.performance_class}}</div>
<div class="category">{{opcDoc.category}}</div>
</div>
<img class="pdf-picture" [src]="imageUrls[0]" alt="" crossorigin="Anonymous" />
</div>
<div class="col-md-7 info">
<h1 class="pdf-title"
[ngClass]="{'title': true, 'title--blue': opcDoc.explorer, 'title--triangle': opcDoc.popular_item }">
{{opcDoc.title}}
</h1>
<div *ngIf="opcDoc.popular_item" class="attribute">{{opcDoc.popular_item_label}}</div>
<div *ngIf="opcDoc.explorer" class="attribute">{{opcDoc.performance_class}}</div>
<p class="category pdf-category">{{opcDoc.category}}</p>
<div *ngIf="tridionData">
<p class="description pdf-description" *ngIf="tridionData.Description">
<rich-text-field [body]="tridionData.Description.BodyText" id="pdf-id-description">
</rich-text-field>
</p>
<div class="row">
<div class="col-md-6 col-sm-12">
<rich-text-field [body]="tridionData?.ColumnOne"></rich-text-field>
</div>
<div class="col-md-6 col-sm-12" [ngClass]="{'col-offset-md-6': !tridionData.ColumnOne}">
<rich-text-field [body]="tridionData?.ColumnTwo"></rich-text-field>
</div>
</div>
<div class="d-flex">
<div class="links" *ngIf="links">
<button class="mr-1 mb-1 btn btn-green cad-download cta-button"
(click)="goToCadDownload()"
*ngIf="cadAvailable">{{ cadDownloadButtonTitle }}</button>
<button class="mr-1 mb-1 btn btn-green cta-button" (click)="autoBtn.onClick()"
*ngFor="let autoBtn of automaticButtons"> {{ autoBtn.labelText }}</button>
<a component-link class="component-link btn btn-green cta-button mr-1 mb-1"
[linkObject]="link" *ngFor="let link of links">
{{ link.title }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="shortcuts" *ngIf="shortcuts">
<div shortcuts-main [entity]="shortcuts"></div>
</div>
<div technical-specification [data]="opcData" *ngIf="opcData"></div>
</div>
<div class="container space-between" *ngIf="information">
<rich-text-field [body]="information"> </rich-text-field>
</div>
<div cad-download [designation]="designation" (available)="setCadDownloadVariables($event)" *ngIf="designation && cadAvailable">
</div>
<ng-container *ngIf="contentForPDPs">
<div dxa-entity [entities]="row" [region]="'MAIN'" *ngFor="let row of contentForPDPs"></div>
</ng-container>
@import "src/app/styles/helpers";
:host {
height: 100%;
}
.pdp {
padding-top: 2rem;
.product-info {
margin-bottom: calc-rem(50);
.container {
.category {
@include font-scale(18, medium);
color: $cool-grey;
}
.attribute
{
@include font-size(14);
}
.image-container {
img {
display: block;
max-height: calc-rem(350);
max-width: 100%;
margin-left: auto;
margin-right: auto;
}
.prod-info {
@include media-breakpoint-up(md) {
display: none;
}
}
}
.title {
@include font-size(42);
display: flex;
align-items: center;
&--blue {
color: $dark-blue;
}
&--triangle:before {
@include font-size(36);
content: "\25ba";
color: $cool-grey;
}
}
.info {
display: flex;
flex-direction: column;
@media (max-width: map-get($grid-breakpoints, md) - 1) {
.title { display: none; }
.category { display: none; }
.attribute { display: none; }
}
.category {
margin-bottom: calc-rem(20);
}
.description {
color: $cool-grey;
margin-bottom: calc-rem(40);
line-height: 1.78;
}
.links {
display: flex;
flex-wrap: wrap;
.cta-button {
margin-right: 4px;
flex: 0 1 100%;
@include media-breakpoint-up(md) {
flex: 0 1 calc(50% - 4px);
}
@include media-breakpoint-up(lg) {
flex: 0 1 calc(33.3% - 4px);
}
}
}
}
}
}
.shortcuts {
background-color: $off-white;
}
}