1(function() { 2 'use strict'; 3 4 const app = angular.module('app'); 5 6 angular.module('app').run(['$rootScope', '$location', '$q', '$timeout', 'shoppingCartModel', 'idstcApiRequests', 'cartService', 'beautysocietyCommonService', 'beautysocietyToastMessageService', 'beautysocietyPosthogService', 'appService', function ($rootScope, $location, $q, $timeout, shoppingCartModel, idstcApiRequests, cartService, beautysocietyCommonService, beautysocietyToastMessageService, beautysocietyPosthogService, appService) { 7 8 // Track previous URL parameters to detect if only sort/page changed 9 let previousUrlParams = { 10 path: $location.path(), 11 search: $location.search()?.search, 12 sort: $location.search()?.sort, 13 pg: $location.search()?.pg 14 }; 15 16 $rootScope.$on('$locationChangeStart', function () { 17 $(document).progressStart(); 18 }); 19 20 $rootScope.$on('$locationChangeSuccess', async function () { 21 const orderUrlParameter = getOrderUrlParameter($location); 22 const searchValue = $location.search()?.search; 23 const currentPath = $location.path(); 24 const currentSort = $location.search()?.sort; 25 const currentPg = $location.search()?.pg; 26 27 // Check for join cart and handle separation from customer cart ONLY when on order page 28 if (orderUrlParameter !== -1) { 29 // Clear join cart and switch to customer cart when navigating to order page 30 cartService.clearJoinCartAndSwitchToCustomerCart(); 31 } 32 33 // Check if only sort or page parameter changed (not category or search) 34 const onlySortOrPageChanged = 35 previousUrlParams.path === currentPath && 36 previousUrlParams.search === searchValue && 37 (previousUrlParams.sort !== currentSort || previousUrlParams.pg !== currentPg); 38 39 // Update previous URL parameters 40 previousUrlParams = { 41 path: currentPath, 42 search: searchValue, 43 sort: currentSort, 44 pg: currentPg 45 }; 46 47 // If only sort or page changed, skip reload to preserve filtered products â unless 48 // there is nothing left to preserve, which means something cleared the list and the 49 // shopper would be left looking at "No products found in this category". 50 if (onlySortOrPageChanged && shoppingCartModel.products?.length) { 51 $(document).progressFinish(); 52 return; 53 } 54 55 // If not on order page and no search, don't load products 56 if (orderUrlParameter === -1 && !searchValue) { 57 $(document).progressFinish(); 58 return; 59 } 60 61 beautysocietyCommonService.setProductsLoaded(false); 62 63 await updateCategoryFromUrl( 64 orderUrlParameter === -1 ? undefined : orderUrlParameter, 65 searchValue 66 ); 67 }); 68 69 function getOrderUrlParameter($location) { 70 const pathParts = $location.path().split('/').filter(part => part.length > 0); 71 const orderIndex = pathParts.findIndex(part => part.toLowerCase() === 'order'); 72 73 if (orderIndex === -1) { 74 return -1; 75 } 76 77 // Return the next part after 'order', or undefined if there isn't one 78 return pathParts[orderIndex + 1] || undefined; 79 } 80 81 async function loadCategories() { 82 const stored = sessionStorage.getItem('displayCategories'); 83 84 if (stored) { 85 return beautysocietyCommonService.getFlattenedCategoriesList(JSON.parse(stored)); 86 } 87 88 try { 89 const response = await idstcApiRequests.getDisplayCategories(); 90 91 sessionStorage.setItem('displayCategories', JSON.stringify(response.data)); 92 93 return beautysocietyCommonService.getFlattenedCategoriesList(response.data);; 94 } catch(error) { 95 return []; 96 } 97 } 98 99 async function updateCategoryFromUrl(urlExtension, searchValue) { 100 let categories = await loadCategories(); 101 102 // If categories aren't loaded yet (race condition), wait briefly and retry once 103 if ((!categories || !categories.length) && !searchValue) { 104 await new Promise(resolve => setTimeout(resolve, 150)); 105 categories = await loadCategories(); 106 } 107 108 // Even if categories fail to load, still load products with default category 109 const normalizedExtension = urlExtension?.toUpperCase()?.replace('%20', ' ') || ''; 110 111 const category = categories?.find(cat => 112 cat.URLExtension?.toUpperCase() === normalizedExtension 113 ); 114 115 const selectedCategoryId = category ? category.DisplayCategoryID : (shoppingCartModel.selectedCategoryId || null); 116 const finalCategory = category || (selectedCategoryId && c
116ategories?.length ? categories.find(cat => cat.DisplayCategoryID === selectedCategoryId) : null); 117 118 clearSearchFilters(finalCategory); 119 120 try { 121 const cartId = await cartService.getCartId(); 122 123 const searchParams = { 124 CartID: cartId, 125 CorporateConsultantURL: idstcSettingCorporateConsultantUrl, 126 LanguageCode: beautysocietyCommonService.getLanguageCode() 127 }; 128 129 if (searchValue) { 130 searchParams.SearchValue = searchValue; 131 132 beautysocietyPosthogService.trackEvent('Products Searched', { 133 query: searchValue 134 }); 135 } else if (normalizedExtension?.toLowerCase() !== 'all-products' && normalizedExtension?.toLowerCase() !== 'allproducts') { 136 if (!urlExtension) { 137 // Default to category ID 1 (all products) when no category specified 138 searchParams.DisplayCategoryID = 1; 139 } else if (finalCategory) { 140 searchParams.DisplayCategoryID = finalCategory.DisplayCategoryID; 141 } else if (selectedCategoryId) { 142 searchParams.DisplayCategoryID = selectedCategoryId; 143 } else { 144 // Default to category ID 1 instead of invalid ID 145 searchParams.DisplayCategoryID = 999999; 146 } 147 } else if(normalizedExtension?.toLowerCase() === 'all-products' || normalizedExtension?.toLowerCase() === 'allproducts') { 148 searchParams.DisplayCategoryID = 1; 149 } 150 151 const categoryProducts = await idstcApiRequests.getProductsForCart(searchParams); 152 153 beautysocietyCommonService.selectedCategory = finalCategory; 154 155 shoppingCartModel.products = categoryProducts?.data; 156 shoppingCartModel.selectedCategory = finalCategory; 157 shoppingCartModel.selectedCategoryId = finalCategory ? finalCategory.DisplayCategoryID : selectedCategoryId; 158 shoppingCartModel.flatCategoriesList = categories; 159 shoppingCartModel.searchText = searchValue; 160 161 beautysocietyPosthogService.productListViewedEvent(null, finalCategory?.Name, categoryProducts?.data); 162 163 $timeout(function() { 164 $rootScope.beautysocietyProducts = categoryProducts?.data ? [...categoryProducts.data] : []; 165 }, 0); 166 } catch(error) { 167 beautysocietyToastMessageService.show(error?.message || 'An error occurred while loading the products.', 'error'); 168 } finally { 169 $(document).progressFinish(); 170 shoppingCartModel.cartItemsUpdating = false; 171 172 // "No products found" hangs off this flag, so it is only raised once the fetch has 173 // finished â and after the products above have been published, or the message would 174 // show for the tick in between. 175 $timeout(function() { 176 beautysocietyCommonService.setProductsLoaded(true); 177 }, 0); 178 } 179 } 180 181 function clearSearchFilters(category) { 182 sessionStorage.removeItem('idstcCartFilter_SearchText'); 183 sessionStorage.removeItem('idstcCartFilterOptions'); 184 sessionStorage.setItem('idstcCartFilter_DisplayCategoryID', category ? category.DisplayCategoryID : null); 185 186 shoppingCartModel.filterOptions = null; 187 shoppingCartModel.unfilteredProducts = null; 188 } 189 }]); 190 191 app.directive('beautisocietyProductListing', function ($q, $location, appService, cartService, shoppingCartModel) { 192 return { 193 restrict: 'E', 194 replace: true, 195 templateUrl: beautysocietyDirectivePluginRelativeUrl + '/assets/views/directives/beautysociety-product-listing-directive.html', 196 controller: function ($scope, $rootScope, $sce, beautysocietyCommonService, $timeout, idstcApiRequests, beautysocietyToastMessageService, beautysocietyPosthogService) { 197 $scope.vm = {}; 198 $scope.dataLoaded = false; 199 $scope.isLoading = false; 200 $scope.products = []; 201 $scope.defaultImageUrl = beautysocietyDirectivePluginBaseUrl + '/assets/images/placeholder.jpg'; 202 $scope.Math = window.Math; 203 $scope.beautysocietyCommonService = beautysocietyCommonService; 204 205 const urlPage = $location.search()?.pg; 206 const cookiePage = parseInt(getCookie('idstcItemCurrentPage') || 1); 207 208 $scope.currentPage = urlPage ? parseInt(urlPage) : cookiePage; 209 $scope.itemsPerPage = 24; 210 $scope.totalItems = 0; 211 $scope.totalPages = 0; 212 $scope.paginatedProducts = []; 213 214 $scope.vm.cartview = localStorage.getItem('idstcItemView') || (typeof getCookie === 'function' ? getCookie('idstcItemView') : null) || 'grid.html'; 215 $scope.vm.shoppingCartModel = shoppingCartModel; 216 $scope.vm.sortByOptions = [{ 217 name: 'displayOrder', 218 displayName: 'Most Popular', 219 sort: 10 220 }, { 221 name: 'price', 222 displayName: 'Price Low to High', 223 sort: 20 224 }, { 225 name: '-price', 226 displayName: 'Price High to Low', 227 sort: 30 228 }, { 229 name: 'displayName', 230 displayName: 'Item Name A-Z', 231 sort: 40 232 }]; 233 234 if (!$scope.vm.shoppingCartModel.sortExpression) { 235 const urlSort = $location.search().sort; 236 const storedSort = localStorage.getItem('idstcItemSortExpression') || (typeof getCookie === 'function' ? getCookie('idstcItemSortExpression') : null); 237 $scope.vm.shoppingCartModel.sortExpression = urlSort || storedSort || 'displayOrder'; 238 shoppingCartModel.sortExpression = $scope.vm.shoppingCartModel.sortExpression; 239 } 240 241 // Ensure sortExpression is always set (like idstc-acuta does) 242 if (!$scope.vm.shoppingCartModel.sortExpression || $scope.vm.shoppingCartModel.sortExpression === '') { 243 $scope.vm.shoppingCartModel.sortExpression = 'displayOrder'; 244 shoppingCartModel.sortExpression = 'displayOrder'; 245 } 246 247 // Sort products based on sortExpression 248 $scope.sortProducts = function() { 249 if (!$scope.products || $scope.products.length === 0) { 250 return; 251 } 252 253 const sortExpression = $scope.vm.shoppingCartModel.sortExpression || 'displayOrder'; 254 const productsCopy = angular.copy($scope.products); 255 256 switch (sortExpression) { 257 case 'price': 258 productsCopy.sort(function (a, b) { 259 if (a.price > b.price) { 260 return 1; 261 } else if (a.price < b.price) { 262 return -1; 263 } 264 return 0; 265 }); 266 break; 267 case '-price': 268 productsCopy.sort(function (a, b) { 269 if (a.price < b.price) { 270 return 1; 271 } else if (a.price > b.price) { 272 return -1; 273 } 274 return 0; 275 }); 276 break; 277 case 'displayName': 278 productsCopy.sort(function (a, b) { 279 const nameA = (a.displayName || a.title || '').toLowerCase(); 280 const nameB = (b.displayName || b.title || '').toLowerCase(); 281 if (nameA > nameB) { 282 return 1; 283 } else if (nameA < nameB) { 284 return -1; 285 } 286 return 0; 287 }); 288 break; 289 case 'displayOrder': 290 default: 291 // Match idstc-acuta sorting: (a.displayOrder - b.displayOrder || a.displayName.localeCompare(b.displayName)) 292 productsCopy.sort(function (a, b) { 293 const orderA = a.displayOrder || a.DisplayOrder || 0;
294 const orderB = b.displayOrder || b.DisplayOrder || 0; 295 296 if (orderA !== orderB) { 297 return orderA - orderB; 298 } 299 300 // If displayOrder is the same, sort by displayName 301 const nameA = a.displayName || a.title || ''; 302 const nameB = b.displayName || b.title || ''; 303 return nameA.localeCompare(nameB); 304 }); 305 break; 306 } 307 308 $scope.products = productsCopy; 309 }; 310 311 // Pagination functions 312 $scope.updatePagination = function() { 313 $scope.sortProducts(); 314 315 $scope.totalItems = $scope.products.length; 316 $scope.totalPages = Math.ceil($scope.totalItems / $scope.itemsPerPage); 317 318 // Reset to page 1 if current page is beyond total pages 319 if ($scope.currentPage > $scope.totalPages && $scope.totalPages > 0) { 320 $scope.currentPage = 1; 321 } 322 323 // Calculate start and end indices 324 const startIndex = ($scope.currentPage - 1) * $scope.itemsPerPage; 325 const endIndex = startIndex + $scope.itemsPerPage; 326 327 // Get paginated products 328 $scope.paginatedProducts = $scope.products.slice(startIndex, endIndex); 329 330 // Initialize badge rotation after pagination update 331 $timeout(function() { 332 initializeBadgeRotation(); 333 }, 100); 334 }; 335 336 $scope.goToPage = function(page) { 337 if (page >= 1 && page <= $scope.totalPages) { 338 $scope.currentPage = page; 339 340 $location.search('pg', page); 341 $scope.updatePagination(); 342 beautysocietyCommonService.scrollToTop(); 343 } 344 }; 345 346 $scope.getPageNumbers = function() { 347 const pages = []; 348 const startPage = Math.max(1, $scope.currentPage - 2); 349 const endPage = Math.min($scope.totalPages, $scope.currentPage + 2); 350 351 for (let i = startPage; i <= endPage; i++) { 352 pages.push(i); 353 } 354 355 return pages; 356 }; 357 358 const loadProducts = function(products) { 359 $scope.isLoading = false; 360 361 // Rendering an empty list is not the same as having loaded nothing: this runs on 362 // init too, before the first fetch has even been made. Whether products are 363 // loaded is decided by the fetch itself. 364 if (!products || !products.length) { 365 $scope.products = []; 366 shoppingCartModel.products = []; 367 $scope.vm.shoppingCartModel = shoppingCartModel; 368 $scope.updatePagination(); 369 $scope.setDataLoaded(true); 370 371 beautysocietyCommonService.applyScope($scope); 372 373 return; 374 } 375 376 $scope.products = beautysocietyCommonService.formatProducts(products); 377 378 beautysocietyCommonService.applyScope($scope); 379 380 shoppingCartModel.products = $scope.products; 381 382 $scope.vm.shoppingCartModel = shoppingCartModel; 383 384 // Restore user's sort preference from storage when products are loaded 385 const storedSort = localStorage.getItem('idstcItemSortExpression') || (typeof getCookie === 'function' ? getCookie('idstcItemSortExpression') : null) || 'displayOrder'; 386 $scope.vm.shoppingCartModel.sortExpression = storedSort; 387 shoppingCartModel.sortExpression = storedSort; 388 389 // updatePagination will call sortProducts() to sort the products 390 $scope.updatePagination(); 391 392 $rootScope.$broadcast('beautySocietyComponentMessage', { 393 action: 'beautysocietyFilterCategoriesLoaded' 394 }); 395 396 $scope.setDataLoaded(true); 397 398 beautysocietyCommonService.applyScope($scope); 399 400 // Initialize badge rotation for products with multiple badges 401 $timeout(function() { 402 initializeBadgeRotation(); 403 }, 500); 404 }; 405 406 // Initialize badge rotation animation 407 function initializeBadgeRotation() { 408 const badgeContainers = document.querySelectorAll('.product-listing-badges-container'); 409
410 badgeContainers.forEach(function(container) { 411 const badges = container.querySelectorAll('.product-listing-badge-item'); 412 if (badges.length <= 1) { 413 return; // No rotation needed for single badge 414 } 415 416 let currentIndex = 0; 417 418 function rotateBadge() { 419 // Hide all badges 420 badges.forEach(function(badge, index) { 421 badge.classList.remove('badge-active'); 422 badge.classList.add('badge-hidden'); 423 }); 424 425 // Show current badge 426 badges[currentIndex].classList.remove('badge-hidden'); 427 badges[currentIndex].classList.add('badge-active'); 428 429 // Move to next badge 430 currentIndex = (currentIndex + 1) % badges.length; 431 } 432 433 // Rotate every 3 seconds 434 const rotationInterval = setInterval(rotateBadge, 3000); 435 436 // Store interval ID on container for cleanup if needed 437 container.setAttribute('data-rotation-interval', rotationInterval); 438 }); 439 } 440 441 $scope.navigateToOrderPage = () => { 442 window.location = '/order'; 443 }; 444 445 $scope.trackProductClick = function(product, event) { 446 beautysocietyCommonService.trackProductClick({ 447 ProductID: product?.productID, 448 ProductSKU: product?.sku, 449 DisplayCategoryID: beautysocietyCommonService?.selectedCategory?.DisplayCategoryID, 450 ProductDisplayName: product?.displayName, 451 GoogleBrand: product?.googleBrand, 452 ProductVariation: product?.variationGroupID, 453 Price: product?.price, 454 QuantityAvailable: product?.quantityAvailable, 455 CouponCode: product?.couponCode, 456 CartUrl: product?.cartUrl 457 }, event); 458 }; 459 460 $scope.vm.sortChanged = function() { 461 const sortExpression = $scope.vm.shoppingCartModel.sortExpression; 462 localStorage.setItem('idstcItemSortExpression', sortExpression); 463 $location.search('sort', sortExpression); 464 $location.search('pg', 1); 465 $scope.currentPage = 1; 466 $scope.updatePagination(); 467 beautysocietyCommonService.scrollToTop(); 468 }; 469 470 const deregisterProductsWatch = $rootScope.$watch('beautysocietyProducts', function(newValue, oldValue) { 471 if (newValue !== oldValue) { 472 loadProducts(newValue || []); 473 } 474 }); 475 476 // Register event listener for other messages (not beautysocietyProductsUpdated) 477 const deregisterMessageListener = $rootScope.$on('beautySocietyComponentMessage', function(event, data) { 478 if (data?.action === 'beautysocietyProductsFiltered') { 479 $scope.setDataLoaded(false); 480 $scope.products = beautysocietyCommonService.formatProducts(data?.products); 481 $scope.currentPage = 1; 482 $scope.updatePagination(); 483 $scope.setDataLoaded(true); 484 beautysocietyCommonService.scrollToTop(); 485 beautysocietyPosthogService.productListFilteredEvent(null, beautysocietyCommonService?.selectedCategory?.Name, data?.selectedFilterOptions, $scope.vm.sortByOptions.find(s => s.name === $scope.vm.shoppingCartModel?.sortExpression) || null, data?.products); 486 } else if (data?.action === 'clearAllFilters') { 487 $scope.setDataLoaded(false); 488 489 const originalProducts = $scope.vm.shoppingCartModel.unfilteredProducts || $scope.vm.shoppingCartModel.products; 490 $scope.products = beautysocietyCommonService.formatProducts(originalProducts); 491 $scope.currentPage = 1; 492 $scope.updatePagination(); 493 $scope.setDataLoaded(true); 494 beautysocietyCommonService.scrollToTop(); 495 beautysocietyPosthogService.productListFilteredEvent(null, beautysocietyCommonService?.selectedCategory?.Name, data?.selectedFilterOptions, $scope.vm.sortByOptions.find(s => s.name === $scope.vm.shoppingCartModel?.sortExpression) || null, []); 496 } else if (data?.action === 'clearFilterOption') { 497 const hasRemainingFilters = $rootScope.beautysocietyFilterOptions?.some(category => 498 category.Options?.some(option => option.Selected) 499 ); 500 501 if (hasRemainingFilters) { 502 // Prevent infinite recursion by using a different action 503 $rootScope.$broadcast('beautySocietyComponentMessage', { 504 action: 'applyRemainingFilters' 505 }); 506 } else { 507 $scope.setDataLoaded(false); 508 const originalProducts = $scope.vm.shoppingCartModel.unfilteredProducts || $scope.vm.shoppingCartModel.products; 509 $scope.products = beautysocietyCommonService.formatProducts(originalProducts); 510 $scope.currentPage = 1; 511 $scope.updatePagination(); 512 $scope.setDataLoaded(true); 513 beautysocietyCommonService.scrollToTop(); 514 beautysocietyPosthogService.productListFilteredEvent(null, beautysocietyCommonService?.selectedCategory?.Name, data?.filterOption ? [data?.f
514ilterOption] : [], $scope.vm.sortByOptions.find(s => s.name === $scope.vm.shoppingCartModel?.sortExpression) || null, $scope.products); 515 } 516 } else if (data?.action === 'dataLoaded') { 517 $scope.setDataLoaded(data?.value); 518 } 519 }); 520 521 // Clean up event listener and watch when scope is destroyed to prevent memory leaks 522 $scope.$on('$destroy', function() { 523 if (deregisterMessageListener) { 524 deregisterMessageListener(); 525 } 526 if (deregisterProductsWatch) { 527 deregisterProductsWatch(); 528 } 529 }); 530 531 $scope.addToCart = (product) => { 532 if (product.quantityAvailable > 0 && product.quantitySafelyAvailable <= 0 && product.safetyStockShowWarning && idstcSettingAcutaCartConfirmAddWhenBelowSafetyStock === 'on' || idstcSettingAcutaCartConfirmAddWhenBelowSafetyStock === 'true') { 533 $scope.showSafetyStockWarningDialog(product); 534 return; 535 } 536 537 $scope.addToCartServer(product); 538 }; 539 540 $scope.showSafetyStockWarningDialog = (product) => { 541 const warningMessage = $scope.prepareSafetyStockWarning(product); 542 543 if (warningMessage) { 544 const continueText = 'Do you wish to continue adding this item to the cart?'; 545 const confirmAddText = warningMessage + '</br></br>' + continueText; 546 547 idstc().dialog({ 548 message: confirmAddText, 549 title: 'Confirm add to cart' 550 }, function (confirm) { 551 if (confirm) { 552 $scope.addToCartServer(product); 553 } 554 }); 555 556 return; 557 } 558 559 $scope.addToCartServer(product); 560 }; 561 562 $scope.prepareSafetyStockWarning = (product) => { 563 let warningMessage = product.safetyStockWarning; 564 565 if (!warningMessage || warningMessage.trim() === '') { 566 warningMessage = 'Quantity is running very low ({{vm.product.QuantityAvailable}} items left)'; 567 } 568 569 return warningMessage.replace('{{vm.product.QuantityAvailable}}', product.quantityAvailable); 570 }; 571 572 $scope.addToCartServer = async (product) => { 573 $(document).progressStart(); 574 575 // Clear join cart and switch to customer cart before adding products 576 cartService.clearJoinCartAndSwitchToCustomerCart(); 577 578 beautysocietyCommonService.addToCartEventToGoogleTagManager({ 579 ProductDisplayName: product.displayName, 580 ProductSKU: product.sku, 581 Price: product.price, 582 GoogleBrand: product.googleBrand, 583 GoogleColor: product.googleColor || product.googleSize, 584 Quantity: 1, 585 BundleVirtualKit: product.bundleVirtualKit 586 }); 587 588 const cartId = await cartService.getCartId(); 589 590 idstcApiRequests.addToCart({ 591 CartID: cartId, 592 SKU: product.sku, 593 Quantity: 1, 594 BundleVirtualKit: product.bundleVirtualKit 595 }).then(function(result) { 596 beautysocietyCommonService.processNotifications(result?.data?.messages); 597 598 idstcApiRequests.getViewCartInfo(true).then(function(response) { 599 beautysocietyCommonService.processNotifications(response?.data?.Notifications); 600 601 beautysocietyCommonService.sendCartToGoogleTagManager(response.data); 602 beautysocietyCommonService.klaviyoAddToCart(response?.data?.OrderLines, product.sku); 603 beautysocietyCommonService.updateCartQuantity(response?.data?.OrderLines); 604 605 beautysocietyPosthogService.productAddedEvent(cartId, { 606 ProductID: product?.productID, 607 ProductSKU: product?.sku, 608 DisplayCategoryID: beautysocietyCommonService?.selectedCategory?.DisplayCategoryID, 609 ProductDisplayName: product?.displayName, 610 GoogleBrand: product?.googleBrand, 611 ProductVariation: product?.variationGroupID, 612 Price: product?.price, 613 QuantityAvailable: product?.quantityAvailable, 614 CouponCode: product?.couponCode, 615 DisplayOrder: product?.displayOrder, 616 ProductCartUrl: product?.cartUrl, 617 ProductImage: product?.pictureUrl 618 }); 619 620 shoppingCartModel.viewCartInfo = response; 621 622 $rootScope.$broadcast('cart.showCart', null); 623 624 const customEvent = new CustomEvent('flightShowCart'); 625 window.dispatchEvent(customEvent); 626 }); 627 }).catch(function(error) { 628 beautysocietyToastMessageService.show(error?.data?.messages, 'error'); 629 }).finally(function() { 630 $(document).progressFinish(); 631 }); 632 }; 633 634 $scope.setGetWishlistsFn = function(theSetDirFn) { 635 $scope.getWishlists = theSetDirFn; 636 }; 637 638 $scope.addToWishlist = async function(product) { 639 if (product.QuantityAvailable === 0) { 640 window.location = '/productdetail/' + (product.cartUrl ||product.sku); 641 return; 642 } 643 644 if (!appService?.loggedIn) { 645 $scope.loginProcess = 'wishlist'; 646 $scope.selectedWishlistProduct = product; 647 $scope.showCustomerLogin = true; 648 $scope.showConsultantLoginLink = false; 649 $scope.showCreatePersonLink = true; 650 $scope.createPersonLink = 'createperson?returnURL=' + encodeURIComponent($location.$$url + ($location.$$url.indexOf('?') >= 0 ? '&' : '?') + 'process=' + $scope.loginProcess + '&createType=petite'); 651 652 $scope.customerLoginWindowListing.center().open(); 653 654 return; 655 } 656 657 const cartId = await cartService.getCartId(); 658 659 $scope.addToWishlistProduct = { 660 CartID: cartId, 661 SKU: product.sku, 662 ProductID: product.productID, 663 BusinessUnitID: product.businessUnitID || product.BusinessUnitID || null, 664 ProductImage: product.pictureUrl, 665 DisplayName: product.displayName, 666 CustomFields: [], 667 BuildableComponents: [], 668 selectedWishlistID: null 669 }; 670 671 $scope.getWishlists(); 672 673 $scope.showCreatePersonLink = true; 674 $scope.createPersonLink = '/productdetail/' + (product.cartUrl || product.sku) + '?returnURL=' + encodeURIComponent($location.$$url + ($location.$$url.indexOf('?') >= 0 ? '&' : '?') + 'process=' + $scope.loginProcess + '&createType=petite'); 675 676 $scope.addToWishlistComplete = false; 677 678 $scope.addProductToWishlistWindow.center().open(); 679 }; 680 681 $scope.$on('customerLoginFinished', function(event, process) { 682 if (process === 'wishlist') { 683 $scope.customerLoginWindowListing.close(); 684 $scope.showCustomerLogin = false; 685 686 $(document).progressFinish(); 687 688 $timeout(function() { 689 if (typeof $scope.getWishlists === 'function') { 690 $scope.getWishlists(); 691 } 692 693 $scope.addToWishlist($scope.selectedWishlistProduct); 694 }, 100); 695 } 696 }); 697 698 $scope.$on('productAddedToWishlist.Complete', function(event, sku) {
699 const product = $scope.products.find(p => p.sku === sku); 700 701 if (product) { 702 product.inWishlist = true; 703 } 704 705 if ($scope.addProductToWishlistWindow) { 706 $scope.addProductToWishlistWindow.close(); 707 } 708 }); 709 710 $scope.gotoWishlist = function (product) { 711 window.location = '/wishlists'; 712 }; 713 714 $scope.setDataLoaded = (value) => { 715 $scope.dataLoaded = value; 716 717 if (!$scope.$$phase && !$scope.$root.$$phase) { 718 try { 719 $scope.$apply(); 720 } catch (e) { 721 // Ignore "already in progress" error 722 } 723 } 724 }; 725 726 $scope.init = async function() { 727 await appService.makeReady(); 728 729 loadProducts($rootScope.beautysocietyProducts || []); 730 }; 731 732 $scope.init(); 733 } 734 }; 735 }); 736})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.