SyncMadeEasy/force-app/main/default/lwc/home/home.js

1550 lines
57 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
****************************************************************************
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 } });
}
}