/* **************************************************************************** Component Name : Home Created Date : 17/4/2024 @description : This Home page serves as the main entry point for the application. * It handles the navigation to all other component pages. This component integrates * with various sub-components and manages the overall layout and * state of the application. @author : SME Team Modification Log: Ver Date Author 1.0 23/4/2024 SME Team **************************************************************************** */ import { LightningElement, track, api, wire } from "lwc"; // DISPLAY-ONLY: used to greet the logged-in user by first name (falling // back to last name) in the support panel. No functional/business logic. import USER_ID from "@salesforce/user/Id"; import { getRecord } from "lightning/uiRecordApi"; import FIRST_NAME_FIELD from "@salesforce/schema/User.FirstName"; import LAST_NAME_FIELD from "@salesforce/schema/User.LastName"; // FETCHING RESOURCES FROM THE STATIC RESOURCES // import pageLoadingGif from "@salesforce/resourceUrl/SMEPageLoadingGif"; // import SyncMadeEasy from "@salesforce/resourceUrl/SyncMadeEasy"; // NOT USING // import ShopifyProcessingGif from "@salesforce/resourceUrl/ShopifyProcessingGif"; // import AuthenticateConnecting from "@salesforce/resourceUrl/AuthenticateConnecting"; // import storeulgetinfo from "@salesforce/resourceUrl/storeulgetinfo"; // import Configuration from "@salesforce/resourceUrl/Configuration"; // import DataProcessing from "@salesforce/resourceUrl/DataProcessing"; // CUSTOM LABELS FOR THE INSERTION import Back from '@salesforce/label/c.Back'; import Setup from '@salesforce/label/c.Setup'; import Shopify_Credentials from '@salesforce/label/c.Shopify_Credentials'; import Object_Setup from '@salesforce/label/c.Object_Setup'; import Record_Type from '@salesforce/label/c.Record_Type'; import Field_Mapping from '@salesforce/label/c.Field_Mapping'; import Finished from '@salesforce/label/c.Finished'; import Contact_support from '@salesforce/label/c.Contact_support'; import Reset_Default_Setting from '@salesforce/label/c.Reset_Default_Setting'; import Having_More_than_One_Store from '@salesforce/label/c.Having_More_than_One_Store'; import Edit_Mapping_Message from '@salesforce/label/c.Edit_Mapping_Message'; import Step_Change_Message from '@salesforce/label/c.Step_Change_Message'; import Store_Change_Lost_Message from '@salesforce/label/c.Store_Change_Lost_Message'; import Warning from '@salesforce/label/c.Warning'; import Error from '@salesforce/label/c.Error'; import Success from '@salesforce/label/c.Success'; import New from '@salesforce/label/c.New'; import Batch_Progress_Message from '@salesforce/label/c.Batch_Progress_Message'; import Go_To_Record_Type from '@salesforce/label/c.Go_To_Record_Type'; import Follow_Steps_Message from '@salesforce/label/c.Follow_Steps_Message'; import Page_Loading_Message from '@salesforce/label/c.Page_Loading_Message'; import Follow_Starting_Steps from '@salesforce/label/c.Follow_Starting_Steps'; import Field_Complete_Message from '@salesforce/label/c.Field_Complete_Message'; import A_maximum_of_1000 from '@salesforce/label/c.A_maximum_of_1000'; import Existing_Store from '@salesforce/label/c.Existing_Store'; import Default_Store_Reset from '@salesforce/label/c.Default_Store_Reset'; import Close from '@salesforce/label/c.Close'; import Thank_You from '@salesforce/label/c.Thank_You'; import Issue_not_sent from '@salesforce/label/c.Issue_Not_Sent'; import Try_Again_Or_Contact_Us from '@salesforce/label/c.Try_Again_Or_Contact_Us'; import Support_Email_Submitted_Message from '@salesforce/label/c.Support_Email_Submitted_Message'; import Previous_Page_Button from '@salesforce/label/c.Previous_Page_Button'; import Go_To_Next_Step_Button from '@salesforce/label/c.Go_To_Next_Step_Button'; import Please_write_your_issue_here from '@salesforce/label/c.Please_write_your_issue_here'; import Submit from '@salesforce/label/c.Submit'; import Issue from '@salesforce/label/c.Issue'; import Cancel from '@salesforce/label/c.Cancel'; import OK from '@salesforce/label/c.OK'; import Shopify_Warning_Modal from "@salesforce/resourceUrl/Shopify_Warning_Modal"; import ShopifyMadeEasyLogo from "@salesforce/resourceUrl/ShopifyMadeEasyLogo"; import SendingMailTrue from "@salesforce/resourceUrl/SendingMailTrue"; import SendingMailFalse from "@salesforce/resourceUrl/SendingMailFalse"; import Resources from "@salesforce/resourceUrl/Resources"; import SMEPageLoadingGIF2 from "@salesforce/resourceUrl/SMEPageLoadingGIF2"; // THIS IS THE PREVIOUS PAGE LOADING GIF // CREATING INSTANCES OF APEX METHODS import issuePkceForState from '@salesforce/apex/PkceUtil.issuePkceForState'; import getConfigrations from '@salesforce/apex/ShopifyController.getConfigrationForMultipleStore'; import updateAutoConfiguration from '@salesforce/apex/ShopifyController.updateAutoConfiguration'; import getAutoConfigurationStatus from '@salesforce/apex/ShopifyController.getAutoConfigurationStatus'; import copyFieldMapping from '@salesforce/apex/ShopifyController.copyFieldMapping'; import remoteSite from '@salesforce/apex/ShopifyController.remoteSite'; import syncShopify from "@salesforce/apex/ShopifyController.syncShopify"; import SendingMailtoShopifySupport from "@salesforce/apex/ShopifyController.sendingMailtoShopifySupport"; import Checkemaildeliverability from "@salesforce/apex/ShopifyController.checkemaildeliverability"; import checkBatchIsProcessing from '@salesforce/apex/ShopifyController.checkBatchIsProcessing'; import runPostInstallScript from "@salesforce/apex/ShopifyController.runPostInstallScript"; // DISPLAY-ONLY: last synced (paid) order/opportunity timestamp for the // shared "Connected & syncing" hero bar shown on every step from Object // Setup onward. import getLastOrderSyncTime from '@salesforce/apex/ShopifyController.getLastOrderSyncTime'; // DISPLAY-ONLY: caps the store-select combobox dropdown to ~3 rows + scroll. import { loadStyle } from 'lightning/platformResourceLoader'; import COMBOBOX_DROPDOWN_SCROLL from '@salesforce/resourceUrl/comboboxDropdownScroll'; // SHOW TOAST EVENT import { ShowToastEvent } from "lightning/platformShowToastEvent"; import Store from "@salesforce/resourceUrl/shopify_store" import Sme_png from "@salesforce/resourceUrl/SME_png" // import SMELOGO from "@salesforce/resourceUrl/SMELOGO" import SMELogoPNG from "@salesforce/resourceUrl/SMELogoPNG" import SupportAgent from "@salesforce/resourceUrl/SupportAgent" import StoreLogo from "@salesforce/resourceUrl/StoreLogo" import SFLogo from "@salesforce/resourceUrl/SFLogo" import SMEPRO from "@salesforce/resourceUrl/SMEPRO" export default class Home extends LightningElement { // Custom in-component toast state (replaces ShowToastEvent) — see showNotification. @track toasts = []; _toastSeq = 0; // GLOBAL VARIABLES @track storeList = []; @track storeOptions = []; selectedStore; @track isModalOpen = false; @api storeName = ""; @api storeSelectionType = ""; @api isNewStore = false; @track Store = Store; @track Sme_png = Sme_png; @track SMELogoPNG = SMELogoPNG; @track SupportAgent = SupportAgent; @track StoreLogo = StoreLogo; @track SFLogo = SFLogo; @track SMEPRO = SMEPRO; // DISPLAY-ONLY: last synced (paid) order/opportunity time for the shared // hero bar's "Last order synced X ago" line. Mirrors the same logic used // in shopifyCredentials — null means this store has never synced a paid // order/opportunity, in which case the line stays hidden. @track _lastSyncedText = ''; _syncTimerId = null; _syncStoreChecked = null; // DISPLAY-ONLY: current user's name for the "Hello, {name}" greeting // in the support panel. Purely presentational, no functional impact. @wire(getRecord, { recordId: USER_ID, fields: [FIRST_NAME_FIELD, LAST_NAME_FIELD] }) currentUser; get greetingName() { const firstName = this.currentUser?.data?.fields?.FirstName?.value; const lastName = this.currentUser?.data?.fields?.LastName?.value; return firstName || lastName || ''; } // LOCAL VARIABLES batchTriggered = false; isAutomatic = false; useAutomatic = true; @track autoConfigSaveTimeout; @track autoConfigJustEnabled = false; @track storeNameFromCredentials = ''; @track autoJumpInProgress = false; @track spinnerClosed = true; @track optionList = []; @track usercurrentvalue = Setup; @track usercurrentpathvalue = ""; @track userselectedpathvalue = Setup; @track hideTopButtons = true; @track hideBottomButtons = true; @track hideLeftButton = true; @track hideRightButton = false; @track processing = false; @track showStore = false; @track contactModel = false; @track issueDescription = ""; @track supportEmailError = false; @track supportEmailSent = false; @track contactSupport = false; @track isSubmit = true; @track batchProcessingStatus = false; @track batchProcessingObject = {}; @track setupComponent = false; @track shopifyCredentialComponent = false; @track objectMappingComponent = false; @track fieldMappingComponent = false; @track recordyTypeComponent = false; @track finishedComponent = false; @track multiStoreModel = false; @track shopifyCredentialMultiStore = false; @track warningModel = false; @track reordtypeSync = false; @track setupColor = "darkGreenColor"; @track credentialColor = "grayColor"; @track objectColor = "grayColor"; @track fieldColor = "grayColor"; @track recordColor = "grayColor"; @track finishColor = "grayColor"; @track currentvaluecheck = true; @track checkpathwithcurrentvalue = true; @track checkMultiStoreIsExisting = true; @track fieldMappings = ""; @track multiStoreButtonClicked = false; @track label = { Back, Contact_support, Go_To_Record_Type, Close, Existing_Store, Default_Store_Reset, Field_Complete_Message, Follow_Starting_Steps, Page_Loading_Message, Follow_Steps_Message, Reset_Default_Setting, Having_More_than_One_Store, Edit_Mapping_Message, Step_Change_Message, Go_To_Next_Step_Button, Store_Change_Lost_Message, Warning, Error, Support_Email_Submitted_Message, Previous_Page_Button, Success, New, Batch_Progress_Message, Thank_You, Issue_not_sent, Try_Again_Or_Contact_Us, Please_write_your_issue_here, Issue, A_maximum_of_1000, Submit, Cancel, OK }; //CREATING IMAGE OBJECT TO USE ON HTML // @track imageLogo = {SyncMadeEasy,DataProcessing,Configuration,pageLoadingGif,storeulgetinfo,AuthenticateConnecting,ShopifyProcessingGif,SendingMailTrue,SendingMailFalse}; @track imageLogo = { SendingMailTrue, SendingMailFalse }; ShopifyMadeEasyLogo = Resources + "/Resources/SyncMadeEasy.png"; // SyncMadeEasy = Resources + "/Resources/SyncMadeEasy.svg"; pageLoadingGif = SMEPageLoadingGIF2; supportPersonImg = Resources + '/Resources/SupportPersonImage.png'; upgradeProImg = Resources + '/Resources/Pro.png'; SyncProImg = Resources + '/Resources/SyncPro.png'; // CONNECTED CALLBACK connectedCallback() { console.log('SYNC MADE EASY INITIALIZED'); this.checkRemoteSiteSettings(); // let labelList = ["Setup", "Shopify Credentials", "Object Setup", "Field Mapping", "Record Type", "Finished"]; let labelList = [Setup, Shopify_Credentials, Object_Setup, Field_Mapping, Record_Type, Finished]; let priority = 1; labelList.map((value) => { this.optionList.push({ label: value, value: value, selected: false, priority: priority++ }); }); this.handleBatchProcessing(); if (this.storeName) { this.fetchAutoConfigurationStatus(this.storeName); } this.template.addEventListener('autoconfigchange', this.handleAutoConfigChange.bind(this)); // Refresh the server-computed "X ago" label every 60s. this._syncTimerId = setInterval(() => { if (this.storeName) this.loadLastOrderSyncTime(); }, 60000); } disconnectedCallback() { if (this._syncTimerId) { clearInterval(this._syncTimerId); this._syncTimerId = null; } } // storeName is set from many places across the wizard (credentials step, // batch processing, dashboard events, etc.), so rather than hook every // assignment site, renderedCallback picks up the change once and fires a // single Apex call per new store — cheap and idempotent. renderedCallback() { if (this.storeName && this.storeName !== this._syncStoreChecked) { this._syncStoreChecked = this.storeName; this.loadLastOrderSyncTime(); } // Load the combobox dropdown height cap once. if (!this._comboScrollLoaded) { this._comboScrollLoaded = true; loadStyle(this, COMBOBOX_DROPDOWN_SCROLL).catch((error) => { console.log('SME: loadStyle combobox scroll error', JSON.stringify(error)); }); } } // DISPLAY-ONLY: fetches the ready-formatted "Last order synced X ago" text // for the current store (time math done server-side in Apex via Datetime.now()). loadLastOrderSyncTime() { getLastOrderSyncTime({ store: this.storeName }) .then((text) => { this._lastSyncedText = text || ''; }) .catch((error) => { console.log('SME: getLastOrderSyncTime error', JSON.stringify(error)); this._lastSyncedText = ''; }); } // Show the "Last order synced …" line ONLY when this store has actually // synced a paid order/opportunity into this org. get showLastSynced() { return !!this._lastSyncedText; } get lastSyncedText() { return this._lastSyncedText || ''; } handleAutoConfigChange(event) { console.log('>>> Auto config change event received: ' + event.detail); this.isAutomatic = event.detail; // Update child components if needed this.updateChildAutoConfigState(this.isAutomatic); } // METHOD IS FOR AUTOMATION OF THE APP changeConfigurationType(event) { console.log('HOME AUTOMATIION ' + event.detail); const newAutoConfigValue = event.detail; this.isAutomatic = newAutoConfigValue; if (this.storeName) { this.saveAutoConfiguration(newAutoConfigValue); } } saveAutoConfiguration(isAuto) { clearTimeout(this.autoConfigSaveTimeout); this.autoConfigSaveTimeout = setTimeout(() => { updateAutoConfiguration({ storeName: this.storeName, isAuto: isAuto }) .then((result) => { console.log('>>> Auto Configuration saved: ' + isAuto + ', Result: ' + result); this.autoConfigJustEnabled = isAuto; }) .catch((error) => { console.error('>>> Error saving Auto Configuration:', error); // Silent fail for auto-save to avoid annoying user }); }, 500); } fetchAutoConfigurationStatus(storeName) { if (!storeName) { this.isAutomatic = false; return Promise.resolve(false); } // Show spinner while checking status this.spinnerClosed = false; this.processing = true; // Direct call without cache - ensures fresh data from database return getAutoConfigurationStatus({ storeName: storeName }) .then((result) => { console.log('>>> FRESH Auto Configuration status for ' + storeName + ': ' + result); // Set auto-config based on LATEST database value this.isAutomatic = (result === true); // Update child components this.updateChildAutoConfigState(this.isAutomatic); return result; }) .catch((error) => { console.error('>>> Error fetching Auto Configuration status:', error); this.isAutomatic = false; return false; }) .finally(() => { this.spinnerClosed = true; this.processing = false; }); } updateChildAutoConfigState(isAuto) { // Try to update child components if they have setter methods const credentialsComp = this.template.querySelector('c-shopify-credentials'); if (credentialsComp && typeof credentialsComp.setAutoConfiguration === 'function') { credentialsComp.setAutoConfiguration(isAuto); } const setupComp = this.template.querySelector('c-setup'); if (setupComp && typeof setupComp.setAutoConfiguration === 'function') { setupComp.setAutoConfiguration(isAuto); } } /** * This Method is called to create remote site settings */ checkRemoteSiteSettings() { console.log('SYNC MADE EASY CREATING REMOTE SITE'); remoteSite().then((result) => { //Success }).catch((error) => { //Error occured }); } // CHECK EXISTING STORE IS SELECTED OR EDITED OR CREATING A NEW STORE checkExistingStoreHandler(event) { this.checkMultiStoreIsExisting = event.detail.subject; this.multiStoreModel = false; this.storeName = event.detail.storeName; this.shopifyCredentialMultiStore = false; this.storeSelectionType = event.detail.storeSelectType; // Reset per-session state so stale values from the previous store // don't bleed into the new store's credentials / object-setup flow. this.batchTriggered = false; this.storeNameFromCredentials = ''; this.autoJumpInProgress = false; // if (this.storeSelectionType.toLowerCase() === this.label.New) { if (this.storeSelectionType === this.label.New) { this.isNewStore = true; this.isAutomatic = false; this.resetChildComponent(); } else { this.isNewStore = false; this.fetchAutoConfigurationStatus(this.storeName) .then(() => { // Only reset component after status is confirmed this.resetChildComponent(); }); return; } } // PATH HANDLER pathHandler(event) { const selectedStep = event.target.value; if (this.batchProcessingStatus) { this.showNotification( this.label.Warning, 'Warning', this.label.Batch_Progress_Message ); return; } // Prevent navigation to disabled steps in auto mode if (this.isAutomatic && (selectedStep === Object_Setup || selectedStep === Field_Mapping)) { this.showNotification( this.label.Warning, 'Warning', 'Object Setup and Field Mapping are Auto Configured' ); return; } // CHECKING IF THE PAGE IS STILL LOADING if (!this.spinnerClosed) { this.showNotification(this.label.Warning, 'Warning', this.label.Page_Loading_Message); return; } // Restrict navigation from Setup if status is not checked if (this.usercurrentvalue === Setup && this.refs.setup && !this.refs.setup.isStatusChecked) { if (selectedStep !== Setup) { this.refs.setup.goNextPage(); return; } } // CHAECKING IF IT IS AN EXISTING STORE OR NOT if (this.spinnerClosed && this.checkMultiStoreIsExisting) { this.childComponentName = []; this.userselectedpathvalue = event.target.value; let currentPriority = 1; let selectedPriority = 1; // HANDLING THE EVENT OCCURES ON PATH HANDLER for (let options of this.optionList) { if (this.usercurrentvalue !== Record_Type && this.userselectedpathvalue === Finished) { this.showNotification(this.label.Error, 'Error', this.label.Go_To_Record_Type); this.userselectedpathvalue = this.usercurrentvalue; return null; } if (this.usercurrentvalue == options.label) { currentPriority = options.priority; } if (this.userselectedpathvalue == options.label) { selectedPriority = options.priority; } } // HANDLE AUTO-CONFIG NAVIGATION FROM CREDENTIALS TO RECORD TYPE if (this.isAutomatic && this.usercurrentvalue === Shopify_Credentials && selectedStep === Record_Type) { // Validate we have store name before allowing direct jump to Record Type if (!this.storeName) { this.showNotification(this.label.Error, 'Error', 'Please save credentials first'); return; } // Set flags for auto-navigation this.autoJumpInProgress = true; this.usercurrentvalue = Record_Type; this.userselectedpathvalue = Record_Type; for (let option of this.optionList) { option.selected = (option.label === Record_Type); } this.resetChildComponent(); return; } // HANDLE FINISH FROM RECORD TYPE IN AUTO MODE if (selectedPriority === 6 && currentPriority === 5) { if (this.isAutomatic) { console.log('>>> Auto mode: Finish path - delegating to save flow'); // DON'T execute batch here! // Just set target and let handleNextButton trigger proper save flow this.userselectedpathvalue = Finished; this.handleNextButton(); return; } this.handleNextButton(); } else { this.optionList.map((options) => { if (this.userselectedpathvalue == options.label) { this.usercurrentvalue = this.userselectedpathvalue; options.selected = true; } else { options.selected = false; } }); } // RESETTING THE PAGE STRUCTURE AND CSS OF THE UI this.resetChildComponent(); } // IF STORE IS NEW IS EDITED BY THE USER else if (this.spinnerClosed && !this.checkMultiStoreIsExisting) { this.userselectedpathvalue = event.target.value; let currentPriority = 1; let selectedPriority = 1; currentPriority = this.optionList.find(obj => obj.label === this.usercurrentvalue)?.priority; selectedPriority = this.optionList.find(obj => obj.label === this.userselectedpathvalue)?.priority; if (currentPriority + 1 === selectedPriority) { // Enforce store existence check for Shopify Credentials if (this.usercurrentvalue === Shopify_Credentials && !this.storeName) { this.showNotification(this.label.Error, 'Error', 'Please save credentials first.'); return; } // Just navigate forward the same way backward navigation works. for (let options of this.optionList) { if (this.userselectedpathvalue == options.label) { this.usercurrentvalue = this.userselectedpathvalue; options.selected = true; } else { options.selected = false; } } this.resetChildComponent(); } else if (selectedPriority < currentPriority) { for (let options of this.optionList) { if (this.userselectedpathvalue == options.label) { this.usercurrentvalue = this.userselectedpathvalue; options.selected = true; } else { options.selected = false; } } this.resetChildComponent(); } else { this.showNotification(this.label.Error, 'Error', this.label.Follow_Steps_Message); } } else { this.showNotification(this.label.Error, 'Error', this.label.Follow_Starting_Steps); } } navigateToFinish() { console.log('>>> navigateToFinish called'); // Set current values to Finish this.usercurrentvalue = Finished; this.userselectedpathvalue = Finished; // Update all option selections for (let option of this.optionList) { option.selected = (option.label === Finished); } // Reset and show finish component this.resetChildComponent(); } // RESETTING VALUE FOR ALL THE COMPONENT BEFORE SETTING THE VALUES AS PER THE CURRENT COMPONENT resetChildComponent() { console.log('>>> resetChildComponent called for: ' + this.usercurrentvalue); // IMMEDIATE CHECK: If auto-config is ON and trying to show disabled pages, skip immediately if (this.isAutomatic && (this.usercurrentvalue === Object_Setup || this.usercurrentvalue === Field_Mapping)) { console.log('>>> AUTO CONFIG: Blocking disabled page ' + this.usercurrentvalue); // Show warning notification this.showNotification( this.label.Warning, 'Warning', 'Object Setup and Field Mapping are Auto Configured' ); // Auto-navigate to next valid page setTimeout(() => { if (this.usercurrentvalue === Object_Setup) { // Skip to Field Mapping (which will also be blocked and skip to Record Type) this.usercurrentvalue = Field_Mapping; this.resetChildComponent(); } else if (this.usercurrentvalue === Field_Mapping) { // Skip to Record Type this.usercurrentvalue = Record_Type; this.userselectedpathvalue = Record_Type; // Update selection for (let option of this.optionList) { option.selected = (option.label === Record_Type); } this.autoJumpInProgress = true; this.resetChildComponent(); } }, 100); return; // Stop here, don't render the disabled component } this.processing = true; this.setupComponent = false; this.shopifyCredentialComponent = false; this.objectMappingComponent = false; this.fieldMappingComponent = false; this.recordyTypeComponent = false; this.finishedComponent = false; this.setColors("grayColor", "grayColor", "grayColor", "grayColor", "grayColor", "grayColor"); this.showChildComponent(); } // SETTING UP THE COLORS FOR THE COMPONENTS AS PER THE SELECTION setColors(setupColor, credentialColor, objectColor, fieldColor, recordColor, finishColor) { this.setupColor = setupColor; this.credentialColor = credentialColor; this.objectColor = objectColor; this.fieldColor = fieldColor; this.recordColor = recordColor; this.finishColor = finishColor; } // BUTTON RENDERING CONTROLLER AS PER THE USER SELECTED PAGE showButtons(showStore, hideTopButtons, hideBottomButtons, hideLeftButton) { this.showStore = showStore; this.hideTopButtons = hideTopButtons; this.hideBottomButtons = hideBottomButtons; this.hideLeftButton = hideLeftButton; } // CHILD COMPONENT CONTROLLER TO SHOW OR HIDE THE CHILD COMPONENT showChildComponent() { for (let option of this.optionList) { if (option.selected) { if (option.label == Setup) { this.setupComponent = true; this.setColors("darkGreenColor", "grayColor", "grayColor", "grayColor", "grayColor", "grayColor"); this.showButtons(false, true, true, true); } else if (option.label == Shopify_Credentials) { this.shopifyCredentialComponent = true; this.setColors("greenColor", "darkGreenColor", "grayColor", "grayColor", "grayColor", "grayColor"); // hideBottomButtons is now true: shopifyCredentials.html has its own // in-card Back / Save & Next footer (added for the reference-mockup // restyle), so home's bottom bar would otherwise duplicate it — same // pattern already used for the Setup step above. this.showButtons(false, true, true, true); } else if (option.label == Object_Setup) { this.objectMappingComponent = true; this.setColors("greenColor", "greenColor", "darkGreenColor", "grayColor", "grayColor", "grayColor"); this.showButtons(true, true, true, true); } else if (option.label == Field_Mapping) { this.fieldMappingComponent = true; this.setColors("greenColor", "greenColor", "greenColor", "darkGreenColor", "grayColor", "grayColor"); this.showButtons(true, true, true, true); } else if (option.label == Record_Type) { this.recordyTypeComponent = true; this.setColors("greenColor", "greenColor", "greenColor", "greenColor", "darkGreenColor", "grayColor"); this.showButtons(true, true, false, false); } else if (option.label == Finished) { this.finishedComponent = true; this.setColors("greenColor", "greenColor", "greenColor", "greenColor", "greenColor", "darkGreenColor"); this.showButtons(false, true, true, false); } else { //Error occurs } } } this.processing = false; } // OPEN SUPPORT MODAL TO EMAIL THE QUERY openContactSupport() { this.contactModel = true; this.lockBodyScroll(); if (!this.spinnerClosed) { this.contactModel = false; this.unlockBodyScroll(); } // CHECKING THE MAIL DELIVERABILITY IF SUPPORTED BY THE USER PERMISSIONS OR NOT Checkemaildeliverability() .then((result) => { if (result) { this.contactSupport = true; } else { this.supportEmailError = true; } }) .catch((error) => { this.supportEmailError = true; }); } // CLOSE SUPPORT MODAL closeModal() { this.contactModel = false; this.unlockBodyScroll(); this.supportEmailSent = false; this.supportEmailError = false; this.contactSupport = false; } // CLOSE STORE CHANGE WARNING MODAL closeWarningModal() { this.warningModel = false; this.unlockBodyScroll(); } // STORE OPERATION HANDLER IF THE USER IS EDITING OR SELECTING THE STORE OR IF THE USER IS CONFIGURING THE NEW STORE configureMultiStore() { this.warningModel = false; this.unlockBodyScroll(); if (this.usercurrentvalue !== Shopify_Credentials) { this.shopifyCredentialMultiStore = true; this.usercurrentvalue = Shopify_Credentials; this.userselectedpathvalue = Shopify_Credentials; for (let option of this.optionList) { if (option.label == this.usercurrentvalue) { option.selected = true; } else { option.selected = false; } } this.resetChildComponent(); } else { this.refs.shopifyCredentials.openMultiStoreController(); } } // BUTTON IS CALLED FROM THE HTML WHICH IS USED TO SEND THE EMAIL TO THE SUPPORT submitDetails() { console.log('I m in submit details method'); // Trim leading and trailing spaces from the issue description const trimmedIssueDescription = this.issueDescription.trim(); // Check if the trimmed issue description is empty if (trimmedIssueDescription === "") { console.log('If Block is executed'); this.contactModel = true; this.lockBodyScroll(); // Display an error message if the issue description is empty after trimming this.showNotification(this.label.Error, 'Error', this.label.Field_Complete_Message); } else { // If the issue description is not empty after trimming, proceed to submit this.contactModel = false; this.unlockBodyScroll(); console.log('Else Block is executed'); this.contactSupport = false; SendingMailtoShopifySupport({ issue: this.issueDescription }) .then((result) => { this.supportEmailSent = true; this.contactModel = false; this.unlockBodyScroll(); this.issueDescription = ""; setTimeout(() => { this.supportEmailSent = false; }, 6000); }) .catch((error) => { t this.contactModel = true; this.lockBodyScroll(); this.supportEmailError = true; }); } console.log('Submit details method end'); } // HANDLING THE ISSUE DESCRIPTION METHOD CALLS FROM THE HTML issueHandler(event) { this.issueDescription = (event.detail && event.detail.value !== undefined) ? event.detail.value : event.target.value; } // METHOD IS CALLED FROM HTML AND IS USED TO OPEN THE WARNING MODAL BEFORE CHANGING THE STORE multipleStores() { if (this.batchProcessingStatus) { this.showNotification(this.label.Warning, 'Warning', this.label.Batch_Progress_Message); } else if (!this.spinnerClosed) { this.warningModel = false; } else if (this.usercurrentvalue === Setup) { this.multiStoreButtonClicked = true; this.refs.setup.goNextPage(); } else { this.warningModel = true; this.lockBodyScroll(); } } // CALLED FROM HTML AND USED TO GO TO THE PREVIOUS COMPONENT previousPage() { if (this.batchProcessingStatus) { this.showNotification(this.label.Warning, 'Warning', this.label.Batch_Progress_Message); } else { let index = 0; for (let option of this.optionList) { if (option.label == this.usercurrentvalue) { index = this.optionList.indexOf(option); option.selected = false; } } // If auto configuration is ON and current page is Record Type, skip to Shopify Credentials if (this.isAutomatic && this.usercurrentvalue === Record_Type) { // Find Shopify Credentials index const credentialsIndex = this.optionList.findIndex(opt => opt.label === Shopify_Credentials); if (credentialsIndex !== -1) { this.optionList[credentialsIndex].selected = true; this.usercurrentvalue = this.optionList[credentialsIndex].label; this.userselectedpathvalue = this.optionList[credentialsIndex].label; } } else { this.optionList[index - 1].selected = true; this.usercurrentvalue = this.optionList[index - 1].label; this.userselectedpathvalue = this.optionList[index - 1].label; } this.resetChildComponent(); } } // CALLED FROM HTML AND USED TO GO TO THE NEXT COMPONENT handleNextButton() { console.log('>>> handleNextButton'); this.ensureStoreName(); if (this.isAutomatic && (this.usercurrentvalue === Object_Setup || this.usercurrentvalue === Field_Mapping)) { this.nextPage(); return; } if (this.usercurrentvalue == Shopify_Credentials) { console.log('>>> handleNextButton 1 block'); this.refs.shopifyCredentials.handleSaveShopifyCredentials(); console.log('>>> handleNextButton creds'); } else if (this.usercurrentvalue == Object_Setup) { console.log('>>> handleNextButton 2 block'); this.refs.objectMapping.handleSave(); console.log('>>> handleNextButton object map'); } else if (this.usercurrentvalue == Field_Mapping) { console.log('>>> handleNextButton 3 block'); console.log('>>> handleNextButton Field Mapping'); this.refs.fieldMappingParent.handleSaveButton(); console.log('>>> handleNextButton Field Mapping'); } else if (this.usercurrentvalue == Record_Type) { console.log('>>> handleNextButton 4 block'); this.refs.recordTypeMapping.handleSaveRecordTypeMapping(); console.log('>>> handleNextButton Record Type'); } else if (this.usercurrentvalue == Setup) { console.log('>>> handleNextButton 5 block'); this.refs.setup.goNextPage(); console.log('>>> handleNextButton setup'); } else { console.log('>>> handleNextButton else and 6 block'); this.nextPage(); console.log('>>> handleNextButton else'); } } // THIS METHOD IS USED TO CONTROLLER THE NEXT PAGE RENDERING nextPage() { console.log('This is executed : ', this.usercurrentvalue); let index = 0; for (let option of this.optionList) { if (option.label == this.usercurrentvalue) { index = this.optionList.indexOf(option); console.log('loop block is executed >>>', this.usercurrentvalue, index); console.log('loop block is executed >>>', this.usercurrentvalue); option.selected = false; } } const previousValue = this.usercurrentvalue; const nextOption = this.optionList[index + 1]; if (!nextOption) { console.error('>>> ERROR: No next option found'); return; } nextOption.selected = true; this.usercurrentvalue = nextOption.label; // HANDLE AUTO-CONFIG BATCH TRIGGER BEFORE FINISH if ( this.isAutomatic && previousValue === Record_Type && this.usercurrentvalue === Finished ) { // If batch already triggered from pathHandler, just show finish page if (this.batchTriggered) { console.log('>>> Batch already triggered from pathHandler, just showing Finish page'); this.resetChildComponent(); return; } // Otherwise trigger batch (for Save & Next button flow) console.log('>>> Auto config: triggering batch before Finish from nextPage'); const storeToSync = this.storeName || this.storeNameFromCredentials; if (!storeToSync) { console.error('>>> ERROR: No store name available for batch in nextPage'); this.showNotification(this.label.Error, 'Error', 'Store name is missing'); // Revert navigation nextOption.selected = false; this.optionList[index].selected = true; this.usercurrentvalue = previousValue; return; } this.batchTriggered = true; syncShopify({ store: storeToSync }) .then(() => { console.log('>>> Batch SUCCESS from nextPage'); this.resetChildComponent(); }) .catch(error => { console.error('>>> Batch ERROR from nextPage', error); this.batchTriggered = false; // Revert navigation on error nextOption.selected = false; this.optionList[index].selected = true; this.usercurrentvalue = previousValue; this.showNotification(this.label.Error, 'Error', 'Batch execution failed'); }); return; // stop normal flow here } this.resetChildComponent(); } ensureStoreName() { if (!this.storeName && this.storeNameFromCredentials) { this.storeName = this.storeNameFromCredentials; console.log('>>> Restored storeName from backup: ' + this.storeName); } return this.storeName; } // Used in multistore handler closeMultiStoreModal(event) { this.multiStoreModel = false; } // METHOD USED TO CLOSE MULTISTORE MODAL closemodal(event) { this.multiStoreModel = false; } // THIS IS THE MULTISTORE HANDLER OPENING METHOD FOR SHOPIFY CREDENTIAL COMPONENT childHandler(event) { this.shopifyCredentialComponent = true; } /** * * METHOD USED TO CALL THE CHILD COMPONENT SAVE DETIALS METHOD BEFORE GOING TO NEXT PAGE */ childSaveEvent(event) { console.log('>>> THIS IS this.isAutomatic :: ' + this.isAutomatic); console.log('>>> THIS IS this.storeName :: ' + this.storeName); console.log('>>> THIS IS CURRENT VALUE :: ' + this.usercurrentvalue); // Capture store name from credentials save if (this.usercurrentvalue == Shopify_Credentials && event.detail) { this.storeName = event.detail; this.storeNameFromCredentials = event.detail; // Backup store name console.log('>>> Store name saved: ' + this.storeName); if (this.isAutomatic) { this.saveAutoConfiguration(true); } } // Handle auto-config navigation from Credentials if (this.isAutomatic && !this.autoJumpInProgress && this.usercurrentvalue === Shopify_Credentials) { // Ensure we have the store name if (!this.storeName && event.detail) { this.storeName = event.detail; } this.saveAutoConfiguration(true); this.autoJumpInProgress = true; this.usercurrentvalue = Record_Type; this.userselectedpathvalue = Record_Type; for (let option of this.optionList) { option.selected = (option.label === Record_Type); } this.resetChildComponent(); return; } // Handle save from Record Type in auto-config mode if (this.isAutomatic && this.usercurrentvalue === Record_Type) { console.log('>>> REAL RECORD TYPE SAVE → running batch'); // Ensure store name is available const storeToSync = this.storeName || this.storeNameFromCredentials; if (!storeToSync) { console.error('>>> ERROR: No store name available for batch'); this.showNotification(this.label.Error, 'Error', 'Store name is missing'); return; } this.autoJumpInProgress = false; this.batchTriggered = true; syncShopify({ store: storeToSync }) .then(() => { console.log('>>> syncShopify SUCCESS'); this.nextPage(); }) .catch(error => { console.error('>>> syncShopify ERROR', error); this.batchTriggered = false; this.showNotification(this.label.Error, 'Error', 'Batch execution failed'); }); return; } // Handle manual mode Record Type save else if (this.usercurrentvalue == Record_Type) { console.log('>>> THIS IS INSIDE RECORD TYPE (Manual) :: ' + this.usercurrentvalue); const storeToSync = this.storeName || this.storeNameFromCredentials; if (!storeToSync) { console.error('>>> ERROR: No store name available for batch'); this.showNotification(this.label.Error, 'Error', 'Store name is missing'); return; } syncShopify({ store: storeToSync }) .then((result) => { console.log('>>> THIS IS syncShopify Result :: '); }) .catch((error) => { console.log('>>> THIS IS syncShopify Error :: '); }); } // Handle Setup page multi-store button if (this.usercurrentvalue === Setup && this.multiStoreButtonClicked) { this.multiStoreButtonClicked = false; this.warningModel = true; } // Normal navigation flow if (this.usercurrentvalue == this.userselectedpathvalue) { this.nextPage(); this.userselectedpathvalue = this.usercurrentvalue; } else if (!this.checkMultiStoreIsExisting && this.usercurrentvalue !== this.userselectedpathvalue) { this.nextPage(); } else { if (this.checkMultiStoreIsExisting) { for (let option of this.optionList) { if (this.userselectedpathvalue == option.label) { option.selected = true; this.usercurrentvalue = this.userselectedpathvalue; } else { option.selected = false; } this.resetChildComponent(); } } else { this.showNotification(this.label.Error, 'Error', this.label.Follow_Starting_Steps); } } } // ??? CHECK WHETHER THIS COMPONENT IS BEING CALLED FROM ANY COMPONENT OR HTML PART savePreviousPageValue() { console.log('>>> savePreviousPageValue'); if (this.usercurrentpathvalue !== this.usercurrentvalue) { this.handleNextButton(); console.log('>>> savePreviousPageValuex inside'); } } /** * THIS METHOD IS CALLED FROM THE FINISH PAGE IF ANY ERROR OCCURED IN BATCH PROCESSING */ finishedSaveEvent(event) { for (let options of this.optionList) { if (event.detail == options.label) { this.usercurrentvalue = options.label; this.userselectedpathvalue = options.label; options.selected = true; } else { options.selected = false; } } this.resetChildComponent(); } // HANDLE WHETHER THE BATHC IS BEING PROCESSING OF NOT handleBatchProcessingEvent(event) { for (let options of this.optionList) { if (event.detail.pathName == options.label) { this.usercurrentvalue = options.label; this.userselectedpathvalue = options.label; this.storeName = event.detail.store; this.batchProcessingStatus = event.detail.batchStatus; options.selected = true; } else { options.selected = false; } } this.resetChildComponent(); } // HANDLE WHEN ALL BATCHES ARE COMPLETED IN FINISH PAGE handleBatchCompleted(event) { if (this.usercurrentvalue === Finished) { for (let options of this.optionList) { if (event.detail == options.label) { this.usercurrentvalue = options.label; this.userselectedpathvalue = options.label; this.batchProcessingStatus = false; options.selected = true; this.storeSelectionType = this.label.Existing_Store; this.checkMultiStoreIsExisting = true; } else { options.selected = false; } } this.resetChildComponent(); } } /* * THIS METHOD IS USED TO SHOW THE TOAST EVENT */ // Custom gradient toast (success = green→teal, error = red) instead of // ShowToastEvent. Same (title, variant, message) signature/callers. showNotification(title, variant, message) { const v = (variant || '').toString().toLowerCase(); const kind = v === 'error' ? 'error' : v === 'success' ? 'success' : v === 'warning' ? 'warning' : 'info'; const id = 't' + (this._toastSeq++); this.toasts = [...this.toasts, { id, title, message, cssClass: 'sw-toast sw-toast-' + kind, iconLabel: kind === 'error' ? '⚠' : kind === 'success' ? '✓' : kind === 'warning' ? '!' : 'ℹ' }]; if (kind !== 'error') { // eslint-disable-next-line @lwc/lwc/no-async-operation setTimeout(() => this._dismissToast(id), 4000); } } _dismissToast(id) { this.toasts = this.toasts.filter((t) => t.id !== id); } handleToastClose(event) { this._dismissToast(event.currentTarget.dataset.id); } /** * * THIS METHOD IS USED TO CLOSE THE SPINNER */ spinnerHandlerTrue() { this.spinnerClosed = false; this.disablePathHandler = true; } /** * * THIS METHOD IS USED TO OPEN THE SPINNER */ spinnerHandlerFalse() { this.spinnerClosed = true; this.disablePathHandler = false; } // METHOD TO HANDLE THE BATCH RUNNING STATUS FROM FINISH PAGE handleFinishBatchStatus(event) { this.batchProcessingStatus = event.detail; } get showStoreFromObjectSetupOnward() { return this.objectMappingComponent || this.fieldMappingComponent || this.recordyTypeComponent // || this.finishedComponent; } // METHOD IS USED TO CHECK IF THE BACTH IS ALREADY RUNNING handleBatchProcessing() { console.log('SYNC MADE EASY: Checking batch details'); checkBatchIsProcessing().then((result) => { this.batchProcessingObject = JSON.parse(result); if (this.batchProcessingObject !== null && this.batchProcessingObject?.HIC_ShopifySync__Batch_Is_Processing__c === true) { this.storeName = this.batchProcessingObject.HIC_ShopifySync__Store__c; this.batchProcessingStatus = this.batchProcessingObject.HIC_ShopifySync__Batch_Is_Processing__c;; // Go to the finish page for (let options of this.optionList) { if (options.label === Finished) { this.usercurrentvalue = options.label; this.userselectedpathvalue = options.label; options.selected = true; } else { options.selected = false; } } this.resetChildComponent(); } else { this.setupComponent = true; } }).catch((error) => { this.showNotification(this.label.Error, 'Error', this.label.Batch_Progress_Message); }); } redirectToSalesforce() { console.log('>>> URL TO INITIALISE '); let clientId = '3MVG9k02hQhyUgQBOt_JP6D3pqjgnrs_fqXqRo3UTb0N952WEZnDenpYrXqh4sFC7vvb6aZQXnD4fURdodCds'; let redirectUrl = 'https://login.salesforce.com/apex/HIC_ShopifySync__SalesforceAuthentication'; // PKCE: Apex generates verifier+challenge and packs the verifier into state. issuePkceForState({ existingState: '' }).then((pkce) => { let urlToOpen = '/services/oauth2/authorize?client_id=' + clientId + '&response_type=code&redirect_uri=' + redirectUrl + '&code_challenge=' + encodeURIComponent(pkce.codeChallenge) + '&code_challenge_method=S256' + '&state=' + encodeURIComponent(pkce.state); console.log('>>> URL TO INITIALISE 3rd ' + urlToOpen); window.open(urlToOpen, '_blank'); }).catch((error) => { console.log('PKCE issuance failed: ' + JSON.stringify(error)); }); // var companyId = event.currentTarget.dataset.realmid1; // isSandox().then(resp => { // //console.log(resp); // if (resp) { // let urlToOpen = '/services/oauth2/authorize?client_id=' + this.sfClientId_Sand + '&response_type=code&redirect_uri=' + this.sfRedrectUriForSandbox + '&state=sandboxtest_realmid=' + companyId; // console.log(urlToOpen); // window.open(urlToOpen); // // } else { // // let urlToOpen = '/services/oauth2/authorize?client_id=' + this.sfClientId_Prod + '&response_type=code&redirect_uri=' + this.sfRedrectUriForProduction + '&state=productionlive_realmid=' + companyId; // // console.log(urlToOpen); // // window.open(urlToOpen); // // } // }); } resetDefaults() { console.log('SYNC MADE EASY: Run Post Install'); if(this.batchProcessingStatus) { this.showNotification(this.label.Warning, 'Warning', this.label.Batch_Progress_Message); }else if(this.refs.setup && !this.refs.setup.isStatusChecked){ this.showNotification(this.label.Warning, 'Warning', "Please complete the Salesforce Configuration Step first"); }else { this.isModalOpen = true; this.lockBodyScroll(); this.getStores(); // runPostInstallScript().then((result) => { // if (result === 'Success') { // console.log('SYNC MADE EASY: Run Post Install Successfull Ran!'); // this.showNotification(this.label.Success, 'Success', this.label.Default_Store_Reset); // } // }).catch((error) => { // console.log('SYNC MADE EASY: Run Post Install Script Failed!'); // this.showNotification(this.label.Error, 'Error', this.label.Error); // }) } } closeCertificateModal() { this.isModalOpen = false; this.unlockBodyScroll(); this.selectedStore = undefined; this.selectedStoreName = null; } getStores() { console.log('Fetching stores...'); console.log('Current working store name:', this.storeName); getConfigrations() .then((result) => { let data = typeof result === 'string' ? JSON.parse(result) : result; console.log('All stores from Apex:', data); const filteredStores = data.filter(store => { const storeLabel = store.Name || store.storeName; return storeLabel !== this.storeName; }); this.storeOptions = filteredStores.map(store => ({ label: store.Name || store.storeName, value: store.Id || store.storeId })); console.log('Final combobox options:', this.storeOptions); }) .catch((error) => { console.error('Error fetching stores:', error); }); } handleStoreChange(event) { const selectedId = event.detail.value; this.selectedStore = selectedId; // 🔍 Get store NAME from options const selectedOption = this.storeOptions.find( option => option.value === selectedId ); this.selectedStoreName = selectedOption?.label; console.log('Selected Store Id:', this.selectedStore); console.log('Selected Store Name:', this.selectedStoreName); } handleCopyMapping() { console.log('setupComponent : ', this.setupComponent); console.log('shopifyCredentialComponent : ', this.shopifyCredentialComponent); console.log('objectMappingComponent : ', this.objectMappingComponent); console.log('fieldMappingComponent : ', this.fieldMappingComponent); console.log('recordyTypeComponent : ', this.recordyTypeComponent); // 🔒 Safety checks if (!this.storeName) { this.showNotification('Error', 'Error', 'Current store is missing'); return; } if (!this.selectedStore) { this.showNotification('Error', 'Error', 'Please select a store'); return; } let pageStatus = ''; if (this.setupComponent) { pageStatus = 'Setup'; this.setupComponent = false; } else if (this.shopifyCredentialComponent) { pageStatus = 'Shopify Credentials'; this.shopifyCredentialComponent = false; } else if (this.objectMappingComponent) { pageStatus = 'Object Mapping'; this.objectMappingComponent = false; } else if (this.fieldMappingComponent) { pageStatus = 'Field Mapping'; this.fieldMappingComponent = false; } else if (this.recordyTypeComponent) { pageStatus = 'Record Type'; this.recordyTypeComponent = false; } this.isModalOpen = false; this.unlockBodyScroll(); // Show spinner and hide other content this.processing = true; this.spinnerClosed = false; console.log('FROM STORE:', this.selectedStoreName); console.log('TO STORE:', this.storeName); copyFieldMapping({ fromStore: this.selectedStoreName, toStore: this.storeName }) .then(() => { this.showNotification( 'Success', 'Success', 'Field mapping copied successfully' ); // Optional UX cleanup this.isModalOpen = false; this.unlockBodyScroll(); this.selectedStore = undefined; this.selectedStoreName = null; }) .catch(error => { console.error(error); this.showNotification( 'Error', 'Error', error.body?.message || 'Failed to copy field mapping' ); }) .finally(() => { if (pageStatus === 'Setup' && !this.setupComponent) { this.setupComponent = true; } else if (pageStatus === 'Shopify Credentials' && !this.shopifyCredentialComponent) { this.shopifyCredentialComponent = true; } else if (pageStatus === 'Object Mapping' && !this.objectMappingComponent) { this.objectMappingComponent = true; } else if (pageStatus === 'Field Mapping' && !this.fieldMappingComponent) { this.fieldMappingComponent = true; } else if (pageStatus === 'Record Type' && !this.recordyTypeComponent) { this.recordyTypeComponent = true; } this.processing = false; this.spinnerClosed = true; }); } lockBodyScroll() { document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.width = '100%'; this.scrollY = window.scrollY; } // UNLOCK BODY SCROLL WHEN MODAL CLOSES unlockBodyScroll() { document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.width = ''; window.scrollTo(0, this.scrollY); } // ======================================================================== // NEW — presentation-only members added for the reference-mockup restyle. // Nothing above this line was changed. These replace the two old // decorative/functional progress-indicator blocks with a single styled // step list, reusing the exact same real state (optionList, the six // *Color trackers) and the real, untouched pathHandler(event) navigation // method — invoked here via a hand-built { target: { value } } object, // the same shape lightning-progress-step's onclick passed. // ======================================================================== // DISPLAY-ONLY: "Setup Progress X / N" label + bar, derived from the // existing optionList/usercurrentvalue. No functional impact. // DISPLAY-ONLY: current calendar year for the copyright footer. get currentYear() { return new Date().getFullYear(); } get setupProgressStepNum() { const current = this.optionList.find(o => o.label === this.usercurrentvalue); return current ? current.priority : 1; } get setupProgressTotal() { return this.optionList.length || 6; } get setupProgressLabel() { return `${this.setupProgressStepNum} / ${this.setupProgressTotal}`; } get setupProgressBarStyle() { const pct = Math.round((this.setupProgressStepNum / this.setupProgressTotal) * 100); return `height:100%;width:${pct}%;border-radius:999px;background:linear-gradient(90deg,#6cb33f,#3f9a86);transition:width .4s;`; } get navSteps() { const colors = [ this.setupColor, this.credentialColor, this.objectColor, this.fieldColor, this.recordColor, this.finishColor ]; const descriptions = [ 'Domain & Security', 'Connect your store', 'Choose what syncs', 'Map your data', 'Assign Record Types', 'Go Live' ]; const icons = ['shield', 'bag', 'layers', 'grid', 'tag', 'check']; return this.optionList.map((option, index) => { const color = colors[index]; const done = color === 'greenColor'; const active = color === 'darkGreenColor'; const icon = icons[index]; return { key: option.label, value: option.value, label: 'Step ' + (index + 1), title: option.label, desc: descriptions[index], done, active, showIcon: !done && !active, isShield: icon === 'shield', isBag: icon === 'bag', isLayers: icon === 'layers', isGrid: icon === 'grid', isTag: icon === 'tag', isCheck: icon === 'check', isLast: index === this.optionList.length - 1, circleClass: done ? 'nav-circle nav-circle_done' : active ? 'nav-circle nav-circle_active' : 'nav-circle', topLineClass: index === 0 ? 'nav-line nav-line_hidden' : ((done || active) ? 'nav-line nav-line_done' : 'nav-line'), lineClass: index === this.optionList.length - 1 ? 'nav-line nav-line_hidden' : (done ? 'nav-line nav-line_done' : 'nav-line'), titleClass: done ? 'nav-title nav-title_done' : active ? 'nav-title nav-title_active' : 'nav-title', chipClass: done ? 'nav-chip nav-chip_done' : active ? 'nav-chip nav-chip_active' : 'nav-chip', labelWrapClass: active ? 'nav-label nav-label_active' : 'nav-label' }; }); } handleNavClick(event) { const value = event.currentTarget.dataset.value; this.pathHandler({ target: { value } }); } }