@colijnit/product 258.1.5 → 258.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { Directive, Injectable, EventEmitter, Component, ViewEncapsulation, Input, Output, Renderer2, ChangeDetectorRef, ViewChild, ElementRef, Pipe, NgModule, ChangeDetectionStrategy, HostListener, HostBinding, SecurityContext, CUSTOM_ELEMENTS_SCHEMA, Inject, PLATFORM_ID, NO_ERRORS_SCHEMA } from '@angular/core';
3
3
  import { __awaiter } from 'tslib';
4
- import { Subject, BehaviorSubject } from 'rxjs';
4
+ import { Subject, BehaviorSubject, combineLatest } from 'rxjs';
5
5
  import { Options } from '@colijnit/ioneconnector/build/model/options';
6
6
  import { ArticleFullObject } from '@colijnit/articleapi/build/model/article-full-object';
7
7
  import { SuperArticle } from '@colijnit/articleapi/build/model/super-article';
@@ -30,8 +30,8 @@ class Version {
30
30
  constructor() {
31
31
  this.name = "@colijnit/product";
32
32
  this.description = "Product detail page project for iOne";
33
- this.symVer = "258.1.5";
34
- this.publishDate = "9-7-2025 14:07:55";
33
+ this.symVer = "258.1.6";
34
+ this.publishDate = "22-8-2025 14:23:38";
35
35
  }
36
36
  }
37
37
 
@@ -204,6 +204,14 @@ class ProductConnectorAdapterService {
204
204
  });
205
205
  });
206
206
  }
207
+ getGeneratedArtDirectly(goodId, showLoader) {
208
+ return __awaiter(this, void 0, void 0, function* () {
209
+ return this.articleConnector.getGeneratedArtDirectly(goodId, showLoader).catch((messages) => {
210
+ this._eventService.errorMessage.next(messages);
211
+ return null;
212
+ });
213
+ });
214
+ }
207
215
  getGoodIdFromArticleNr(sku) {
208
216
  return __awaiter(this, void 0, void 0, function* () {
209
217
  const response = yield this.articleConnector.getGoodIdFromArticleNr(sku);
@@ -571,6 +579,7 @@ class ProductConnectorService {
571
579
  this._adapterService = _adapterService;
572
580
  this._settingsService = _settingsService;
573
581
  this.controllerInitialized = new BehaviorSubject(false);
582
+ this.articleLoaded = new BehaviorSubject(false);
574
583
  this._initializing = false;
575
584
  this._initialized = false;
576
585
  this.connectorOptions = new Options();
@@ -648,6 +657,9 @@ class ProductConnectorService {
648
657
  getJsonArticleFlatTree(goodId, goodType, quantity, externalSource = false, showLoader = true, configuratorStatistics) {
649
658
  return this._adapterService.getJsonArticleFlatTree(goodId, goodType, quantity, externalSource, showLoader, this._instanceId, configuratorStatistics);
650
659
  }
660
+ getGeneratedArtDirectly(goodId, showLoader = true) {
661
+ return this._adapterService.getGeneratedArtDirectly(goodId, showLoader);
662
+ }
651
663
  addWebSessionTransactionLine(transactionUuid, sku, quantity) {
652
664
  return __awaiter(this, void 0, void 0, function* () {
653
665
  return yield this._adapterService.addWebSessionTransactionLine(transactionUuid, sku, quantity);
@@ -981,9 +993,18 @@ class ProductPageComponent {
981
993
  if (this.selections.nativeElement) {
982
994
  this.selections.nativeElement.forceRenderImage();
983
995
  }
984
- }), this.settingsService.settingsLoaded.subscribe(loaded => this._handleSettingsLoaded(loaded)), this._ione.controllerInitialized.subscribe((initialized) => {
985
- this.settingsLoaded = initialized;
986
- }), this.appEventService.onAnswersAvailable.subscribe((answers) => {
996
+ }), this.settingsService.settingsLoaded.subscribe(loaded => this._handleSettingsLoaded(loaded)), this._pageLoadedSubscription = combineLatest([
997
+ this._ione.controllerInitialized,
998
+ this._ione.articleLoaded
999
+ ]).subscribe(([initialized, articleLoaded]) => {
1000
+ if (initialized && articleLoaded) {
1001
+ this.settingsLoaded = initialized;
1002
+ }
1003
+ }),
1004
+ // this._ione.controllerInitialized.subscribe((initialized: boolean) => {
1005
+ // this.settingsLoaded = initialized;
1006
+ // }),
1007
+ this.appEventService.onAnswersAvailable.subscribe((answers) => {
987
1008
  if (answers && this.configurable) {
988
1009
  this.showAddToCart = false;
989
1010
  }
@@ -1063,6 +1084,7 @@ class ProductPageComponent {
1063
1084
  if (this._initializedSub) {
1064
1085
  this._initializedSub.unsubscribe();
1065
1086
  }
1087
+ this._pageLoadedSubscription.unsubscribe();
1066
1088
  this.configurator = undefined;
1067
1089
  this.selections = undefined;
1068
1090
  this.fullscreenbutton = undefined;
@@ -1094,7 +1116,8 @@ class ProductPageComponent {
1094
1116
  this._ione.getFullArticle(this._sku).then((article) => {
1095
1117
  this.article = article;
1096
1118
  if (this.article) {
1097
- this.configurable = this.article.goodType === 'B';
1119
+ this.configurable = this.article.goodType === 'B' && (this.article.isConfigurable || this.article.isConfigurable === undefined);
1120
+ // this.configurable = this.article.goodType === 'B';
1098
1121
  if (this.configurable) {
1099
1122
  this.showAddToCart = false;
1100
1123
  this.currentView = SelectorType.TwoD;
@@ -1111,6 +1134,7 @@ class ProductPageComponent {
1111
1134
  });
1112
1135
  this._changeDetector.detectChanges();
1113
1136
  }
1137
+ this._ione.articleLoaded.next(true);
1114
1138
  });
1115
1139
  }
1116
1140
  }));
@@ -1127,7 +1151,7 @@ class ProductPageComponent {
1127
1151
  ProductPageComponent.decorators = [
1128
1152
  { type: Component, args: [{
1129
1153
  selector: 'app-product-page',
1130
- template: "<ng-container *ngIf=\"settingsLoaded\">\r\n <div class=\"page-wrapper\">\r\n <div class=\"page-wrapper-content\">\r\n <div class=\"page-wrapper-left\">\r\n <div class=\"product-image-container\">\r\n <div class=\"product-page-block-selector-type\">\r\n <app-product-selector-type class=\"default-padding\"\r\n [(currentType)]=\"currentView\"\r\n [show2D]=\"configurable\"\r\n [show3D]=\"threeD\"\r\n ></app-product-selector-type>\r\n </div>\r\n <div class=\"product-page-block-image default-padding\" [class.full]=\"fullScreen\">\r\n\r\n <app-image-carousel\r\n *ngIf=\"!enableRenderCarousel\"\r\n [@toggleVisibilityByState]=\"show2D ? 'show' : 'hide'\"\r\n [images]=\"article?.images\"\r\n [showRefresh]=\"configurable && threeD\">\r\n </app-image-carousel>\r\n\r\n <app-render-carousel\r\n *ngIf=\"enableRenderCarousel\"\r\n [@toggleVisibilityByState]=\"show2D ? 'show' : 'hide'\"\r\n [article]=\"article\">\r\n </app-render-carousel>\r\n\r\n <ng-container *ngIf=\"settingsLoaded\">\r\n <threed-configurator\r\n #configurator\r\n class=\"threed-configurator\"\r\n [class.configurator-full-screen]=\"fullScreen\"\r\n [@toggleVisibilityByState]=\"show3D ? 'show' : 'hide'\"\r\n [@toggleFullScreen]=\"fullScreen ? 'fullscreen' : 'halfscreen'\">\r\n </threed-configurator>\r\n </ng-container>\r\n\r\n <co-icon #fullscreenbutton class=\"fullscreen-button\" *ngIf=\"show3D\"\r\n [@toggleTopLeft]=\"fullScreen ? 'fullscreen' : 'halfscreen'\"\r\n [iconData]=\"iconCache.getIcon(fullScreenIcon)\"\r\n (click)=\"showFullScreen()\"></co-icon>\r\n <co-icon class=\"threed-watermark\" *ngIf=\"show3D && fullScreen\"\r\n [iconData]=\"iconCache.getIcon(icon.Logo)\"></co-icon>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-right\">\r\n <div class=\"product-page-block-description\">\r\n <app-product-description class=\"default-padding\" [article]=\"article\"></app-product-description>\r\n </div>\r\n <div class=\"product-page-block-additional\">\r\n <div class=\"product-page-block-additional-description\">\r\n <app-product-additional-description class=\"default-padding\" [article]=\"article\"></app-product-additional-description>\r\n </div>\r\n <div class=\"product-page-block-price\" [class.full]=\"configuring\">\r\n <app-product-price class=\"s-padding\" *ngIf=\"!configuring\"\r\n [pricing]=\"article?.pricing\"\r\n [configurable]=\"configurable\"\r\n ></app-product-price>\r\n <ng-container *ngIf=\"settingsLoaded\">\r\n <threed-selections #selections class=\"threed-selections\"\r\n [class.show-selections]=\"configuring\"\r\n [@toggleFullScreenRight]=\"fullScreen ? 'fullscreen' : 'halfscreen'\"\r\n [class.default-padding]=\"!fullScreen\"\r\n [class.show-full-screen]=\"fullScreen\"\r\n [class.mini-scrollbar]=\"fullScreen\"\r\n [sku]=\"sku\"\r\n [settings]=\"settings\"\r\n (onUserActionFromThreeD)=\"configuring = true\"\r\n (instanceSet)=\"setInstance($event)\"\r\n (onImageReceived)=\"appEventService.onImageReceived.next($event)\"\r\n (onRenderStarted)=\"appEventService.onRenderStarted.next()\"\r\n (onDraftRenderImageReceived)=\"appEventService.onDraftRenderImageReceived.next($event)\"\r\n (onArticleReceived)=\"appEventService.onArticleReceived.next($event.detail)\"\r\n (onSelectionsReceived)=\"appEventService.onSelectionsReceived.next($event.detail)\"\r\n (onArticleInfoReceived)=\"appEventService.onArticleInfoReceived.next($event.detail)\"\r\n (onAnswersAvailable)=\"appEventService.onAnswersAvailable.next($event.detail)\"\r\n (onReadyToRender)=\"appEventService.onReadyToRender.next($event.detail)\"\r\n (onRenderImageReceived)=\"appEventService.onRenderImageReceived.next($event.detail)\"\r\n ></threed-selections>\r\n </ng-container>\r\n </div>\r\n <div class=\"product-page-block-addtocart no-padding product-action-buttons\" *ngIf=\"!configuring\" [@toggleTopRight]=\"fullScreen ? 'fullscreen' : 'halfscreen'\">\r\n <app-product-addtocart\r\n [configurable]=\"configurable\"\r\n [createFrozenArticle]=\"createFrozenArticle\"\r\n [configuring]=\"configuring\"\r\n [showAddToCart]=\"showAddToCart\"\r\n [article]=\"article\"\r\n [externalSource]=\"externalSource\"\r\n [isReturn]=\"isReturn\"\r\n (startConfiguration)=\"handleStartConfiguration()\"\r\n (showRelatedPopup)=\"handlePopUpChange($event)\"\r\n ></app-product-addtocart>\r\n </div>\r\n <div class=\"addtocart-reserved product-action-buttons\" *ngIf=\"configuring\" [class.full-screen]=\"fullScreen\">\r\n <app-product-addtocart class=\"default-padding\"\r\n [configurable]=\"false\"\r\n [fullscreen]=\"fullScreen\"\r\n [showAddToCart]=\"showAddToCart\"\r\n [configuring]=\"configuring\"\r\n [article]=\"article\"\r\n [externalSource]=\"externalSource\"\r\n [isReturn]=\"isReturn\"\r\n (startConfiguration)=\"handleStartConfiguration()\"\r\n ></app-product-addtocart>\r\n </div>\r\n <div class=\"product-page-block-variants\">\r\n <app-product-related class=\"no-padding\" *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\" [refType]=\"64\" [label]=\"'VARIANTS' | localize\"></app-product-related>\r\n </div>\r\n <div class=\"product-page-block-stock\">\r\n <app-product-stock class=\"no-padding\" [goodId]=\"article?.goodId\" (openStockEvent)=\"openStock()\"></app-product-stock>\r\n </div>\r\n <div class=\"product-page-block-delivery\">\r\n <app-product-delivery class=\"no-padding\" [stockAndDelivery]=\"stockAndDelivery\"></app-product-delivery>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-content\">\r\n <div class=\"page-wrapper-left\">\r\n <app-product-info-tabs class=\"no-padding\" [article]=\"article\"></app-product-info-tabs>\r\n </div>\r\n <div class=\"page-wrapper-right\">\r\n <div class=\"product-page-block-related s-padding\">\r\n <app-product-related\r\n *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"1\"\r\n [label]=\"'RELATED_PRODUCTS' | localize\">\r\n </app-product-related>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-content no-top-margin\">\r\n <div class=\"page-wrapper-full\">\r\n <div class=\"product-page-block-alternatives s-padding\">\r\n <app-product-related\r\n *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"4\"\r\n [isSmallModus]=\"false\"\r\n [label]=\"'ALTERNATIVE_PRODUCTS' | localize\">\r\n </app-product-related>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n <div *ngIf=\"showRelatedProductsPopup\">\r\n <co-product-dialog\r\n [mainArticle]=\"article\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"1\"\r\n (closeRelatedPopup)=\"handlePopUpChange($event)\"\r\n >\r\n </co-product-dialog>\r\n\r\n <!-- <app-product-related class=\"no-padding\" *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"-->\r\n <!-- [articles]=\"article?.relatedArticles\" [refType]=\"64\" [label]=\"'VARIANTS' | localize\"></app-product-related>-->\r\n </div>\r\n\r\n\r\n </div>\r\n</ng-container>\r\n",
1154
+ template: "<ng-container *ngIf=\"settingsLoaded\">\r\n <div class=\"page-wrapper\">\r\n <div class=\"page-wrapper-content\">\r\n <div class=\"page-wrapper-left\">\r\n <div class=\"product-image-container\">\r\n <div class=\"product-page-block-selector-type\">\r\n <app-product-selector-type class=\"default-padding\"\r\n [(currentType)]=\"currentView\"\r\n [show2D]=\"configurable\"\r\n [show3D]=\"threeD\"\r\n ></app-product-selector-type>\r\n </div>\r\n <div class=\"product-page-block-image default-padding\" [class.full]=\"fullScreen\">\r\n\r\n <app-image-carousel\r\n *ngIf=\"!enableRenderCarousel\"\r\n [@toggleVisibilityByState]=\"show2D ? 'show' : 'hide'\"\r\n [images]=\"article?.images\"\r\n [showRefresh]=\"configurable && threeD\">\r\n </app-image-carousel>\r\n\r\n <app-render-carousel\r\n *ngIf=\"enableRenderCarousel\"\r\n [@toggleVisibilityByState]=\"show2D ? 'show' : 'hide'\"\r\n [article]=\"article\">\r\n </app-render-carousel>\r\n\r\n <ng-container *ngIf=\"settingsLoaded && this.configurable\">\r\n <threed-configurator\r\n #configurator\r\n class=\"threed-configurator\"\r\n [class.configurator-full-screen]=\"fullScreen\"\r\n [@toggleVisibilityByState]=\"show3D ? 'show' : 'hide'\"\r\n [@toggleFullScreen]=\"fullScreen ? 'fullscreen' : 'halfscreen'\">\r\n </threed-configurator>\r\n </ng-container>\r\n\r\n <co-icon #fullscreenbutton class=\"fullscreen-button\" *ngIf=\"show3D\"\r\n [@toggleTopLeft]=\"fullScreen ? 'fullscreen' : 'halfscreen'\"\r\n [iconData]=\"iconCache.getIcon(fullScreenIcon)\"\r\n (click)=\"showFullScreen()\"></co-icon>\r\n <co-icon class=\"threed-watermark\" *ngIf=\"show3D && fullScreen\"\r\n [iconData]=\"iconCache.getIcon(icon.Logo)\"></co-icon>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-right\">\r\n <div class=\"product-page-block-description\">\r\n <app-product-description class=\"default-padding\" [article]=\"article\"></app-product-description>\r\n </div>\r\n <div class=\"product-page-block-additional\">\r\n <div class=\"product-page-block-additional-description\">\r\n <app-product-additional-description class=\"default-padding\" [article]=\"article\"></app-product-additional-description>\r\n </div>\r\n <div class=\"product-page-block-price\" [class.full]=\"configuring\">\r\n <app-product-price class=\"s-padding\" *ngIf=\"!configuring\"\r\n [pricing]=\"article?.pricing\"\r\n [configurable]=\"configurable\"\r\n ></app-product-price>\r\n <ng-container *ngIf=\"settingsLoaded\">\r\n <threed-selections #selections class=\"threed-selections\"\r\n [class.show-selections]=\"configuring\"\r\n [@toggleFullScreenRight]=\"fullScreen ? 'fullscreen' : 'halfscreen'\"\r\n [class.default-padding]=\"!fullScreen\"\r\n [class.show-full-screen]=\"fullScreen\"\r\n [class.mini-scrollbar]=\"fullScreen\"\r\n [sku]=\"sku\"\r\n [settings]=\"settings\"\r\n (onUserActionFromThreeD)=\"configuring = true\"\r\n (instanceSet)=\"setInstance($event)\"\r\n (onImageReceived)=\"appEventService.onImageReceived.next($event)\"\r\n (onRenderStarted)=\"appEventService.onRenderStarted.next()\"\r\n (onDraftRenderImageReceived)=\"appEventService.onDraftRenderImageReceived.next($event)\"\r\n (onArticleReceived)=\"appEventService.onArticleReceived.next($event.detail)\"\r\n (onSelectionsReceived)=\"appEventService.onSelectionsReceived.next($event.detail)\"\r\n (onArticleInfoReceived)=\"appEventService.onArticleInfoReceived.next($event.detail)\"\r\n (onAnswersAvailable)=\"appEventService.onAnswersAvailable.next($event.detail)\"\r\n (onReadyToRender)=\"appEventService.onReadyToRender.next($event.detail)\"\r\n (onRenderImageReceived)=\"appEventService.onRenderImageReceived.next($event.detail)\"\r\n ></threed-selections>\r\n </ng-container>\r\n </div>\r\n <div class=\"product-page-block-addtocart no-padding product-action-buttons\" *ngIf=\"!configuring\" [@toggleTopRight]=\"fullScreen ? 'fullscreen' : 'halfscreen'\">\r\n <app-product-addtocart\r\n [configurable]=\"configurable\"\r\n [createFrozenArticle]=\"createFrozenArticle\"\r\n [configuring]=\"configuring\"\r\n [showAddToCart]=\"showAddToCart\"\r\n [article]=\"article\"\r\n [externalSource]=\"externalSource\"\r\n [isReturn]=\"isReturn\"\r\n (startConfiguration)=\"handleStartConfiguration()\"\r\n (showRelatedPopup)=\"handlePopUpChange($event)\"\r\n ></app-product-addtocart>\r\n </div>\r\n <div class=\"addtocart-reserved product-action-buttons\" *ngIf=\"configuring\" [class.full-screen]=\"fullScreen\">\r\n <app-product-addtocart class=\"default-padding\"\r\n [configurable]=\"false\"\r\n [fullscreen]=\"fullScreen\"\r\n [showAddToCart]=\"showAddToCart\"\r\n [configuring]=\"configuring\"\r\n [article]=\"article\"\r\n [externalSource]=\"externalSource\"\r\n [isReturn]=\"isReturn\"\r\n (startConfiguration)=\"handleStartConfiguration()\"\r\n ></app-product-addtocart>\r\n </div>\r\n <div class=\"product-page-block-variants\">\r\n <app-product-related class=\"no-padding\" *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\" [refType]=\"64\" [label]=\"'VARIANTS' | localize\"></app-product-related>\r\n </div>\r\n <div class=\"product-page-block-stock\">\r\n <app-product-stock class=\"no-padding\" [goodId]=\"article?.goodId\" (openStockEvent)=\"openStock()\"></app-product-stock>\r\n </div>\r\n <div class=\"product-page-block-delivery\">\r\n <app-product-delivery class=\"no-padding\" [stockAndDelivery]=\"stockAndDelivery\"></app-product-delivery>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-content\">\r\n <div class=\"page-wrapper-left\" *ngIf=\"article\">\r\n <app-product-info-tabs class=\"no-padding\" [article]=\"article\"></app-product-info-tabs>\r\n </div>\r\n <div class=\"page-wrapper-right\">\r\n <div class=\"product-page-block-related s-padding\">\r\n <app-product-related\r\n *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"1\"\r\n [label]=\"'RELATED_PRODUCTS' | localize\">\r\n </app-product-related>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"page-wrapper-content no-top-margin\">\r\n <div class=\"page-wrapper-full\">\r\n <div class=\"product-page-block-alternatives s-padding\">\r\n <app-product-related\r\n *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"\r\n [externalSource]=\"externalSource\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"4\"\r\n [isSmallModus]=\"false\"\r\n [label]=\"'ALTERNATIVE_PRODUCTS' | localize\">\r\n </app-product-related>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n <div *ngIf=\"showRelatedProductsPopup\">\r\n <co-product-dialog\r\n [mainArticle]=\"article\"\r\n [articles]=\"article?.relatedArticles\"\r\n [refType]=\"1\"\r\n (closeRelatedPopup)=\"handlePopUpChange($event)\"\r\n >\r\n </co-product-dialog>\r\n\r\n <!-- <app-product-related class=\"no-padding\" *ngIf=\"article?.relatedArticles && article?.relatedArticles.length > 0\"-->\r\n <!-- [articles]=\"article?.relatedArticles\" [refType]=\"64\" [label]=\"'VARIANTS' | localize\"></app-product-related>-->\r\n </div>\r\n\r\n\r\n </div>\r\n</ng-container>\r\n",
1131
1155
  animations: [
1132
1156
  trigger('toggleFullScreen', [
1133
1157
  state('fullscreen', style({ 'position': 'fixed', 'top': '0', 'left': '0', 'width': '100%', 'height': '100%' })),
@@ -1314,6 +1338,7 @@ class ImageCarouselComponent {
1314
1338
  this._appEventService = _appEventService;
1315
1339
  this._changeDetector = _changeDetector;
1316
1340
  this._domSanitizer = _domSanitizer;
1341
+ this.isPopupOpen = false;
1317
1342
  this.showRefresh = false;
1318
1343
  this.resizing = false;
1319
1344
  this.imageViewModels = [];
@@ -1343,6 +1368,12 @@ class ImageCarouselComponent {
1343
1368
  this._changeDetector.detectChanges();
1344
1369
  }));
1345
1370
  }
1371
+ // Close on ESC
1372
+ onEsc() {
1373
+ if (this.isPopupOpen) {
1374
+ this.closePopup();
1375
+ }
1376
+ }
1346
1377
  set images(value) {
1347
1378
  if (value && value.length > 0) {
1348
1379
  this._images = this._filterValidImages(value);
@@ -1389,15 +1420,16 @@ class ImageCarouselComponent {
1389
1420
  }
1390
1421
  handleShowImage(imageViewModel) {
1391
1422
  if (imageViewModel && imageViewModel.originalSource) {
1392
- let popupWindow = window.open('', 'Image zoom', 'width=600,height=400');
1393
- // Set the content of the popup window
1394
- popupWindow.document.write('<html><head><title>Image zoom</title></head><body>');
1395
- popupWindow.document.write(`<img src=${imageViewModel.originalSource} alt="Image" style="width:100%; height:auto;">`);
1396
- popupWindow.document.write('</body></html>');
1397
- // Close the document to render the popup window
1398
- popupWindow.document.close();
1423
+ this.selectedImage = imageViewModel;
1424
+ this.isPopupOpen = true;
1425
+ this._changeDetector.markForCheck();
1399
1426
  }
1400
1427
  }
1428
+ closePopup() {
1429
+ this.isPopupOpen = false;
1430
+ this.selectedImage = undefined;
1431
+ this._changeDetector.markForCheck();
1432
+ }
1401
1433
  _filterValidImages(value) {
1402
1434
  if (!value) {
1403
1435
  return [];
@@ -1440,38 +1472,54 @@ class ImageCarouselComponent {
1440
1472
  }
1441
1473
  _resizeAndSanitizeSource(source, imageViewModel) {
1442
1474
  const resizeCanvas = document.createElement('canvas');
1443
- const resizeCanvasContext = resizeCanvas.getContext('2d');
1444
- const resizeImage = document.createElement('img');
1445
- resizeImage.crossOrigin = 'anonymous';
1446
- resizeImage.onload = () => __awaiter(this, void 0, void 0, function* () {
1447
- resizeCanvasContext.imageSmoothingEnabled = true;
1448
- resizeCanvasContext.imageSmoothingQuality = 'high';
1449
- // Get the original image dimensions
1450
- const originalWidth = resizeImage.width;
1451
- const originalHeight = resizeImage.height;
1452
- // Calculate the aspect ratio
1453
- const aspectRatio = originalWidth / originalHeight;
1454
- // Calculate the new width and height while maintaining the aspect ratio
1455
- let newWidth = this._resizeCanvasHeight;
1456
- let newHeight = this._resizeCanvasHeight;
1457
- if (originalWidth > originalHeight) {
1458
- newHeight = this._resizeCanvasHeight / aspectRatio;
1475
+ const ctx = resizeCanvas.getContext('2d');
1476
+ const img = document.createElement('img');
1477
+ img.crossOrigin = 'anonymous';
1478
+ img.onload = () => {
1479
+ ctx.imageSmoothingEnabled = true;
1480
+ ctx.imageSmoothingQuality = 'high';
1481
+ const ow = img.width;
1482
+ const oh = img.height;
1483
+ const aspect = ow / oh;
1484
+ let newW = this._resizeCanvasHeight;
1485
+ let newH = this._resizeCanvasHeight;
1486
+ if (ow > oh) {
1487
+ newH = this._resizeCanvasHeight / aspect;
1459
1488
  }
1460
1489
  else {
1461
- newWidth = this._resizeCanvasHeight * aspectRatio;
1462
- }
1463
- // Set the canvas size to the new width and height
1464
- resizeCanvas.width = newWidth;
1465
- resizeCanvas.height = newHeight;
1466
- const imageWidth = this._resizeCanvasHeight * (resizeImage.height / resizeImage.width);
1467
- resizeCanvasContext.drawImage(resizeImage, 0, 0, newWidth, newHeight);
1468
- const resizedSource = resizeCanvas.toDataURL('image/jpeg');
1490
+ newW = this._resizeCanvasHeight * aspect;
1491
+ }
1492
+ resizeCanvas.width = Math.round(newW);
1493
+ resizeCanvas.height = Math.round(newH);
1494
+ // Ensure transparent background before drawing
1495
+ ctx.clearRect(0, 0, resizeCanvas.width, resizeCanvas.height);
1496
+ ctx.drawImage(img, 0, 0, resizeCanvas.width, resizeCanvas.height);
1497
+ const mime = this._detectPreferredMime(source);
1498
+ const resizedSource = mime === 'image/jpeg'
1499
+ ? resizeCanvas.toDataURL('image/jpeg', 0.92) // only if original was JPEG
1500
+ : resizeCanvas.toDataURL(mime); // PNG/WebP keep alpha
1469
1501
  imageViewModel.source = this._domSanitizer.bypassSecurityTrustUrl(resizedSource);
1470
1502
  imageViewModel.originalSource = source;
1471
1503
  this._changeDetector.detectChanges();
1472
- });
1473
- // @ts-ignore
1474
- resizeImage.src = source;
1504
+ };
1505
+ img.src = source;
1506
+ }
1507
+ _detectPreferredMime(source) {
1508
+ // Data URI check
1509
+ const m = source.match(/^data:(image\/[a-zA-Z+.-]+);base64,/);
1510
+ if (m) {
1511
+ const t = m[1].toLowerCase();
1512
+ if (t === 'image/png' || t === 'image/webp' || t === 'image/jpeg')
1513
+ return t;
1514
+ }
1515
+ const lower = source.toLowerCase();
1516
+ if (lower.endsWith('.png'))
1517
+ return 'image/png';
1518
+ if (lower.endsWith('.webp'))
1519
+ return 'image/webp';
1520
+ if (lower.endsWith('.jpg') || lower.endsWith('.jpeg'))
1521
+ return 'image/jpeg';
1522
+ return 'image/png';
1475
1523
  }
1476
1524
  _scrollCarouselToIndex() {
1477
1525
  if (this.currentIndex > -1 && this.currentIndex <= this.images.length) {
@@ -1521,9 +1569,31 @@ ImageCarouselComponent.decorators = [
1521
1569
  </co-scroll-container>
1522
1570
  </div>
1523
1571
  </div>
1572
+ <!-- Modal (real popup) -->
1573
+ <div
1574
+ class="image-modal"
1575
+ *ngIf="isPopupOpen"
1576
+ (click)="closePopup()"
1577
+ role="dialog"
1578
+ aria-modal="true"
1579
+ aria-label="Image preview"
1580
+ >
1581
+ <div class="image-modal__content" (click)="$event.stopPropagation()">
1582
+ <button
1583
+ class="image-modal__close"
1584
+ type="button"
1585
+ aria-label="Close"
1586
+ (click)="closePopup()"
1587
+ >
1588
+ ×
1589
+ </button>
1590
+
1591
+ <img [src]="selectedImage?.originalSource" alt="Image preview" />
1592
+ </div>
1593
+ </div>
1524
1594
  `,
1525
1595
  changeDetection: ChangeDetectionStrategy.OnPush,
1526
- styles: [":host{max-height:540px;height:100%;position:relative}:host:not(.resizing) .inner-carousel{scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scroll-snap-type:x mandatory}#product_page_carousel{position:relative}#product_page_carousel .refresh-button{position:absolute;bottom:10px;right:10px;background:#fff}#product_page_carousel .refresh-button.loading{animation:spin 1s linear infinite}#product_page_carousel .refresh-button:hover{box-shadow:none;background:#74B77F;transition:all .2s ease-in-out}#product_page_carousel .refresh-button:hover ::ng-deep svg path{fill:#fff!important}#product_page_carousel #product_page_carousel_items{position:relative;margin-bottom:10px}#product_page_carousel #product_page_carousel_items ::ng-deep co-loader{position:absolute}#product_page_carousel .inner-carousel{display:flex;flex-direction:row;align-items:center;overflow:hidden;max-height:500px;border:1px solid #efefef}#product_page_carousel .carousel-item{max-height:500px;width:100%;display:flex;cursor:zoom-in;flex-shrink:0;flex-grow:0}#product_page_carousel .carousel-item img{width:100%;height:auto;-o-object-fit:contain;object-fit:contain}#product_page_carousel .carousel-scroller-layer{height:100%;width:100%;position:absolute;pointer-events:none;top:0;left:0}#product_page_carousel #product_page_carousel_thumbs{display:flex;justify-content:flex-start;height:80px;margin-left:auto;margin-right:auto}#product_page_carousel #product_page_carousel_thumbs ::ng-deep co-scroll-container{padding:0 22px}#product_page_carousel #product_page_carousel_thumbs ::ng-deep co-scroll-container .content-wrapper{padding:0}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb{opacity:1;cursor:pointer;transition:all .2s ease;padding:4px;border:1px solid #f6f5f4}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb.active,#product_page_carousel #product_page_carousel_thumbs .carousel-thumb:hover{border-color:#22313c}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb:not(:last-child){margin-right:10px}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb img{height:68px}@media screen and (max-width: 650px){#product_page_carousel_thumbs{height:57px!important}#product_page_carousel_thumbs .carousel-thumb img{height:50px!important}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"]
1596
+ styles: [":host{max-height:540px;height:100%;position:relative}:host:not(.resizing) .inner-carousel{scroll-behavior:smooth;-webkit-overflow-scrolling:touch;scroll-snap-type:x mandatory}#product_page_carousel{position:relative}#product_page_carousel .refresh-button{position:absolute;bottom:10px;right:10px;background:#fff}#product_page_carousel .refresh-button.loading{animation:spin 1s linear infinite}#product_page_carousel .refresh-button:hover{box-shadow:none;background:#74B77F;transition:all .2s ease-in-out}#product_page_carousel .refresh-button:hover ::ng-deep svg path{fill:#fff!important}#product_page_carousel #product_page_carousel_items{position:relative;margin-bottom:10px}#product_page_carousel #product_page_carousel_items ::ng-deep co-loader{position:absolute}#product_page_carousel .inner-carousel{display:flex;flex-direction:row;align-items:center;overflow:hidden;max-height:500px;border:1px solid #efefef}#product_page_carousel .carousel-item{max-height:500px;width:100%;display:flex;cursor:zoom-in;flex-shrink:0;flex-grow:0}#product_page_carousel .carousel-item img{width:100%;height:auto;-o-object-fit:contain;object-fit:contain}#product_page_carousel .carousel-scroller-layer{height:100%;width:100%;position:absolute;pointer-events:none;top:0;left:0}#product_page_carousel #product_page_carousel_thumbs{display:flex;justify-content:flex-start;height:80px;margin-left:auto;margin-right:auto}#product_page_carousel #product_page_carousel_thumbs ::ng-deep co-scroll-container{padding:0 22px}#product_page_carousel #product_page_carousel_thumbs ::ng-deep co-scroll-container .content-wrapper{padding:0}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb{opacity:1;cursor:pointer;transition:all .2s ease;padding:4px;border:1px solid #f6f5f4}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb.active,#product_page_carousel #product_page_carousel_thumbs .carousel-thumb:hover{border-color:#22313c}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb:not(:last-child){margin-right:10px}#product_page_carousel #product_page_carousel_thumbs .carousel-thumb img{height:68px}.image-modal{position:fixed;inset:0;background:rgba(0,0,0,.8);display:flex;align-items:center;justify-content:center;z-index:1000}.image-modal__content{position:relative;max-width:90vw;max-height:90vh}.image-modal__content img{max-width:90vw;max-height:90vh;-o-object-fit:contain;object-fit:contain;display:block}.image-modal__close{position:fixed;top:15px;right:30px;background:transparent;border:none;color:#fff;font-size:60px;line-height:1;cursor:pointer}@media screen and (max-width: 650px){#product_page_carousel_thumbs{height:57px!important}#product_page_carousel_thumbs .carousel-thumb img{height:50px!important}}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"]
1527
1597
  },] }
1528
1598
  ];
1529
1599
  ImageCarouselComponent.ctorParameters = () => [
@@ -1534,6 +1604,7 @@ ImageCarouselComponent.ctorParameters = () => [
1534
1604
  ];
1535
1605
  ImageCarouselComponent.propDecorators = {
1536
1606
  carousel: [{ type: ViewChild, args: ['carousel', { read: ElementRef },] }],
1607
+ onEsc: [{ type: HostListener, args: ['document:keydown.escape',] }],
1537
1608
  showRefresh: [{ type: Input }],
1538
1609
  images: [{ type: Input }],
1539
1610
  handleWindowResize: [{ type: HostListener, args: ['window:resize',] }],
@@ -1782,11 +1853,12 @@ ProductPriceModule.decorators = [
1782
1853
  ];
1783
1854
 
1784
1855
  class ProductAddtocartComponent {
1785
- constructor(iconCache, _ioneControllerService, _appEventService, _settingsService) {
1856
+ constructor(iconCache, _ioneControllerService, _appEventService, _settingsService, _productConnectorAdapterService) {
1786
1857
  this.iconCache = iconCache;
1787
1858
  this._ioneControllerService = _ioneControllerService;
1788
1859
  this._appEventService = _appEventService;
1789
1860
  this._settingsService = _settingsService;
1861
+ this._productConnectorAdapterService = _productConnectorAdapterService;
1790
1862
  this.icon = IconEnum;
1791
1863
  this.createFrozenArticle = true;
1792
1864
  this.configurable = false;
@@ -1826,9 +1898,14 @@ class ProductAddtocartComponent {
1826
1898
  return __awaiter(this, void 0, void 0, function* () {
1827
1899
  if (this.createFrozenArticle) {
1828
1900
  if (this.article.goodType === 'B') {
1829
- const article = yield this._getJSONFromArticleObject({ article: this.article, quantity: quantity });
1830
- if (article) {
1831
- this._appEventService.onAddToCart.next({ article: article, quantity: quantity });
1901
+ if (this.article.isConfigurable) {
1902
+ const article = yield this._getJSONFromArticleObject({ article: this.article, quantity: quantity });
1903
+ if (article) {
1904
+ this._appEventService.onAddToCart.next({ article: article, quantity: quantity });
1905
+ }
1906
+ }
1907
+ else {
1908
+ yield this._handleGeneratedArticleDirectly(quantity);
1832
1909
  }
1833
1910
  }
1834
1911
  else {
@@ -1845,8 +1922,15 @@ class ProductAddtocartComponent {
1845
1922
  }
1846
1923
  }
1847
1924
  else {
1848
- const article = this._ioneControllerService.convertArticleFullObjectToArticleExtended(this.article);
1849
- this._appEventService.onAddToCart.next({ article: article, quantity: quantity });
1925
+ if (this.article.goodType === 'B') {
1926
+ if (!this.article.isConfigurable) {
1927
+ yield this._handleGeneratedArticleDirectly(quantity);
1928
+ }
1929
+ }
1930
+ else {
1931
+ const article = this._ioneControllerService.convertArticleFullObjectToArticleExtended(this.article);
1932
+ this._appEventService.onAddToCart.next({ article: article, quantity: quantity });
1933
+ }
1850
1934
  }
1851
1935
  });
1852
1936
  }
@@ -1855,6 +1939,21 @@ class ProductAddtocartComponent {
1855
1939
  this._appEventService.onAddToQuote.next(yield this._getJSONFromArticleObject({ article: this.article, quantity: quantity }));
1856
1940
  });
1857
1941
  }
1942
+ _handleGeneratedArticleDirectly(quantity) {
1943
+ return __awaiter(this, void 0, void 0, function* () {
1944
+ const generatedGoodId = yield this._getGeneratedArtDirectly(this.article.goodId);
1945
+ if (generatedGoodId) {
1946
+ const articleFull = yield this._productConnectorAdapterService.getArticleFullObject(generatedGoodId, true);
1947
+ const article = this._ioneControllerService.convertArticleFullObjectToArticleExtended(articleFull);
1948
+ this._appEventService.onAddToCart.next({ article: article, quantity: quantity });
1949
+ }
1950
+ });
1951
+ }
1952
+ _getGeneratedArtDirectly(goodId) {
1953
+ return __awaiter(this, void 0, void 0, function* () {
1954
+ return yield this._ioneControllerService.getGeneratedArtDirectly(goodId, true);
1955
+ });
1956
+ }
1858
1957
  _getJSONFromArticleObject(article) {
1859
1958
  return __awaiter(this, void 0, void 0, function* () {
1860
1959
  const configuratorStatistics = new ConfiguratorStatisticsEnvironment();
@@ -1909,7 +2008,8 @@ ProductAddtocartComponent.ctorParameters = () => [
1909
2008
  { type: IconCacheService },
1910
2009
  { type: ProductConnectorService },
1911
2010
  { type: ProductEventService },
1912
- { type: ProductSettingsService }
2011
+ { type: ProductSettingsService },
2012
+ { type: ProductConnectorAdapterService }
1913
2013
  ];
1914
2014
  ProductAddtocartComponent.propDecorators = {
1915
2015
  addToCartButton: [{ type: ViewChild, args: ['addtocartbutton', { read: ElementRef },] }],
@@ -2300,7 +2400,7 @@ class ProductInfoTabsComponent {
2300
2400
  this.updateHeight(tabIndex);
2301
2401
  }
2302
2402
  updateHeight(tabIndex) {
2303
- if (this.tabContent.nativeElement.children.length > 0) {
2403
+ if (this.tabContent && this.tabContent.nativeElement.children.length > 0) {
2304
2404
  if (this.tabContent.nativeElement.children[tabIndex] !== undefined) {
2305
2405
  this.activeTabHeight = this.tabContent.nativeElement.children[tabIndex].offsetHeight;
2306
2406
  }