/* **************************************************************************** Component Name : Setup Created Date : 17/4/2024 @description : This component is used for setting up the application @author : SME Team Modification Log: Ver Date Author 1.0 23/4/2024 SME Team **************************************************************************** */ import { LightningElement, track, api, wire } from "lwc"; // FETCHING RESOURCES FROM THE STATIC RESOURCES // import AuthenticateConnecting from "@salesforce/resourceUrl/AuthenticateConnecting"; // import pageLoadingGif from "@salesforce/resourceUrl/SMEPageLoadingGif"; import Resources from "@salesforce/resourceUrl/Resources"; import SMEPageLoadingGIF2 from "@salesforce/resourceUrl/SMEPageLoadingGIF2"; // MAKING INSTANCE OF APEX CLASS METHODS import saveSiteUrlOnHicConfig from "@salesforce/apex/ShopifyController.saveSiteUrlOnHicConfig"; import issuePkceForState from "@salesforce/apex/PkceUtil.issuePkceForState"; import getUIThemeDescription from "@salesforce/apex/ShopifyController.getUIThemeDescription"; import returnDomainUrl from "@salesforce/apex/ShopifyController.returnDomainUrl"; import CheckSitefromConfiguration from "@salesforce/apex/ShopifyController.checkSitefromConfiguration"; import savesiteurlconfig from "@salesforce/apex/ShopifyController.savesiteurlconfig"; import getSiteURLs from "@salesforce/apex/ShopifyController.getSiteURLs"; import getAuthenticateURL from "@salesforce/apex/ShopifyController.getAuthenticateURL"; import checkSiteClassandPageAccess from "@salesforce/apex/ShopifyController.checkSiteClassandPageAccess"; import setupAccessCheck from "@salesforce/apex/ShopifyController.setupAccessCheck"; import refreshTokenCheck from "@salesforce/apex/ShopifyController.refreshTokenCheck"; import preAuthSetupCheck from "@salesforce/apex/ShopifyController.preAuthSetupCheck"; // import runPostInstallScript from "@salesforce/apex/ShopifyController.runPostInstallScript"; // SHOW TOAST import { ShowToastEvent } from "lightning/platformShowToastEvent"; // NAVIGATION — Locker/LWS-safe way to open Salesforce setup pages. // Raw window.open(url,'_blank') to relative setup URLs is blocked/sanitized in // installed (managed-package) orgs and throws the opaque "aura_prod.js Script error. null". import { NavigationMixin } from "lightning/navigation"; //Custom Labels import click_here from '@salesforce/label/c.click_here'; import Information from '@salesforce/label/c.Information'; import Please from '@salesforce/label/c.Please'; import create_Domain_remote_site from '@salesforce/label/c.create_Domain_remote_site'; import to_create_remote_site_setting from '@salesforce/label/c.to_create_remote_site_setting'; import Attention_required from '@salesforce/label/c.Attention_required'; import create_Authenticate_remote_site from '@salesforce/label/c.create_Authenticate_remote_site'; import self_signed_Certificate_Shopify from '@salesforce/label/c.self_signed_Certificate_Shopify'; import self_signed_Certificate from '@salesforce/label/c.self_signed_Certificate'; import Please_press_the_button_labeled from '@salesforce/label/c.Please_press_the_button_labeled'; import Let_get_started from '@salesforce/label/c.Let_get_started'; import Close from '@salesforce/label/c.Close'; import Site from '@salesforce/label/c.Site'; import SetupLabel from '@salesforce/label/c.Setup'; import How_to from '@salesforce/label/c.How_to'; import Please_create_below_remote from '@salesforce/label/c.Please_create_below_remote'; import No_certificate_found from '@salesforce/label/c.No_certificate_found'; import Create_certificate from '@salesforce/label/c.Create_certificate'; import button_to_create_certificate from '@salesforce/label/c.button_to_create_certificate'; import Please_click_on_the from '@salesforce/label/c.Please_click_on_the'; import If_you_are_a_first_time_visitor from '@salesforce/label/c.If_you_are_a_first_time_visitor'; import Thank_You from '@salesforce/label/c.Thank_You'; import Check_Status from '@salesforce/label/c.Check_Status'; import Next from '@salesforce/label/c.Next'; import Error from '@salesforce/label/c.Error'; import Some_error_occurred_in_theme_description from '@salesforce/label/c.Some_error_occurred_in_theme_description'; import Some_error_occurred_in_site_url from '@salesforce/label/c.Some_error_occurred_in_site_url'; import Some_error_occurred_in_domain_url from '@salesforce/label/c.Some_error_occurred_in_domain_url'; import Some_error_occurred_while_checking_site_configuration from '@salesforce/label/c.Some_error_occurred_while_checking_site_configuration'; import Some_error_occurred_while_checking_site_page_access from '@salesforce/label/c.Some_error_occurred_while_checking_site_page_access'; import Some_error_occurred_while_saving_site_url from '@salesforce/label/c.Some_error_occurred_while_saving_site_url'; import Some_error_occurred_in_refresh_token from '@salesforce/label/c.Some_error_occurred_in_refresh_token'; import Please_create_new_site_or_activate_existing_site from '@salesforce/label/c.Please_create_new_site_or_activate_existing_site'; import Please_configure_site from '@salesforce/label/c.Please_configure_site'; import Please_create_domain_Url_remote_site_setting from '@salesforce/label/c.Please_create_domain_Url_remote_site_setting'; import Please_create_authenticate_Url_remote_site_setting from '@salesforce/label/c.Please_create_authenticate_Url_remote_site_setting'; import Please_create_self_signed_certificate from '@salesforce/label/c.Please_create_self_signed_certificate'; import to_configure_site from '@salesforce/label/c.to_configure_site'; // GO NEXT SITE import Go_Next from '@salesforce/label/c.Go_Next'; import Cancel from '@salesforce/label/c.Cancel'; import Select_a_Site_URL from '@salesforce/label/c.Select_a_Site_URL'; import Select_Progress from '@salesforce/label/c.Select_Progress'; import Success from '@salesforce/label/c.Success'; import Save from '@salesforce/label/c.Save'; import button from '@salesforce/label/c.button'; import Site_Page from '@salesforce/label/c.Site_Page'; // import Please_activate_the_site_or_grant_access from '@salesforce/label/c.Please_activate_the_site_or_grant_access'; import site_currently_does_not_have_access_to_the_class from '@salesforce/label/c.site_currently_does_not_have_access_to_the_class'; import Please_Setup_Site from '@salesforce/label/c.Please_Setup_Site'; import There_is_no_activate_site_in_your_org from '@salesforce/label/c.There_is_no_activate_site_in_your_org'; import to_create_a_new_site_or_activate_existing_site from '@salesforce/label/c.to_create_a_new_site_or_activate_existing_site'; import You_Can_Change_Site from '@salesforce/label/c.You_Can_Change_Site'; import Initialize from '@salesforce/label/c.Initialize'; import Create_Remote_site from '@salesforce/label/c.Create_Remote_site'; // ── Module-level flag: tracks whether the "auto-advance to Step 2 on refresh" // has already run for this page session. Persists across component instances // (so clicking back to Step 1 in the stepper does NOT re-trigger auto-advance), // but resets on browser refresh (module re-loads → false again). let hasAutoAdvancedOnRefresh = false; export default class Setup extends NavigationMixin(LightningElement) { // Instance flag distinguishing an automatic page-load/refresh verification // run from a manual "Check Status" click — see connectedCallback and // handleCheckStatusClick. Only a run made with this true is allowed to // auto-advance to Step 2. _isAutoLoadCheck = false; // Custom in-component toast state (replaces ShowToastEvent) — see // showNotification. @track toasts = []; _toastSeq = 0; // GLOBAL VARIABLE @api storeName = ""; // LOCAL VARIABLE @track url = ""; @track height = "900px"; @track width = "100%"; @track toolingRemoteSiteSettings = false; @track error; @track selectedSite = null; @track CertificateCreateLink = "/lightning/setup/CertificatesAndKeysManagement/page?address=/0P1/e?MasterLabel=Shopify&DeveloperName=Shopify"; // Org My Domain (e.g. https://mydomain.my.salesforce.com). Prepended to relative setup // URLs so window.open resolves correctly in installed/managed-package (Locker) orgs. @track orgDomainUrl = ""; @track viewMode = ""; @track CreateRemotesiteSettingUrl = ""; @track remoteSiteExist = false; @track remoteSiteError = false; @track remoteSiteHeaderMsg = ""; @track certificateExist = false; @track certificateError = false; @track certificateHeaderMsg = ""; @track isCertificate = false; @track checkSiteExist = false; @track checkSiteAccess = false; @track siteURLList = []; @track urlSavedSuccess = false; @track siteModel = false; @track siteOptions = []; @track siteValue; @track siteurl = ""; @track siteSavingNotification = Please_Setup_Site; // DISPLAY-ONLY: suppress the default "Please Setup Site" placeholder in the // Salesforce Configuration Site modal. Real notifications (save success, // access errors, "You can change site") still render normally. get showSiteNotification() { const note = this.siteSavingNotification; return !!note && note !== Please_Setup_Site; } @track siteName = ""; @track allSettingCompleted = false; @track RemotesiteUrl; @track isRemoteSite = false; @track noRefreshToken = false; @track refreshTokenValue = ''; @track accessToken = ''; @track isLoading = false; @track noAccessToken = false; @track authenticationUrl = 'https://login.salesforce.com'; @track authenticationRemoteSite = false; @api isStatusChecked = false; @track selectedSiteId = null; @track disableDomainRemoteSite = true; @track disableAuthenticateRemoteSite = true; @track disableCertificate = true; @track isSiteSupportedOrg = true; checkStatusSpinner = false; @track showCheckStatusMessage = true; //Custom Labels @track label = { click_here ,Cancel, Please ,Information, create_Domain_remote_site , to_create_remote_site_setting , Attention_required , create_Authenticate_remote_site , self_signed_Certificate_Shopify , self_signed_Certificate , Please_press_the_button_labeled , Let_get_started , Close , Site , SetupLabel , How_to , Please_create_below_remote , No_certificate_found , Create_certificate , button_to_create_certificate , Please_click_on_the , If_you_are_a_first_time_visitor , Thank_You , Check_Status , Next , Error , Some_error_occurred_in_theme_description , Some_error_occurred_in_site_url , Some_error_occurred_in_domain_url , Some_error_occurred_while_checking_site_configuration , Some_error_occurred_while_checking_site_page_access , Some_error_occurred_while_saving_site_url, Some_error_occurred_in_refresh_token, Please_create_new_site_or_activate_existing_site, Please_configure_site, Please_create_domain_Url_remote_site_setting, Please_create_authenticate_Url_remote_site_setting, Please_create_self_signed_certificate , to_configure_site , Go_Next , Select_a_Site_URL , Select_Progress , Success , Save , Site_Page , Please_activate_the_site_or_grant_access , site_currently_does_not_have_access_to_the_class , Please_Setup_Site , There_is_no_activate_site_in_your_org , to_create_a_new_site_or_activate_existing_site , You_Can_Change_Site , Initialize , button , Create_Remote_site }; // AuthenticateConnecting = AuthenticateConnecting; // pageLoadingGif = pageLoadingGif; AuthenticateConnecting = Resources + "/Resources/AuthenticateConnecting.gif"; pageLoadingGif = SMEPageLoadingGIF2; // CONNECTED CALLBACK connectedCallback() { console.log('SYNC MADE EASY: Setup Connected Callback'); this.showSpinnerHandler(); // Mark this specific verification run as the automatic one triggered by // page load/refresh (as opposed to the user manually clicking "Check // Status" — see handleCheckStatusClick). Only THIS run is allowed to // auto-advance to Step 2 if everything's already verified. this._isAutoLoadCheck = true; this.getSiteUrl(); this.returnDomain(); this.getThemeDescription(); this.getOrganizationAuthenticateUrl(); } // Manual "Check Status" button click: run the same verification chain as // the automatic page-load check, but never auto-advance off of it — the // user should land on Step 2 automatically only via a fresh page load/ // refresh when setup is already verified, not from clicking this button. handleCheckStatusClick() { this._isAutoLoadCheck = false; this.getSiteUrl(); } //CHECK IF THIS METHOD IS BEING USED ANYWHERE deprecated getOrganizationAuthenticateUrl() { getAuthenticateURL().then((result) => { if (result) { this.authenticationUrl = result; } }).catch((error) => { //Error occured }) } //Check All Settings checkSettingStatus() { this.siteConfiguration(); } //GET TOOLING REMOTE SITE SETTINGS handleToolingremotesite() { this.openSetupPage("/lightning/setup/CustomDomain/home"); } // GET CURRENT UI THEME TO SEND USED IN REDIRECTION getThemeDescription() { getUIThemeDescription().then((result) => { this.viewMode = result; }) .catch((error) => { this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_in_theme_description); }); } // GET ALL SITE URLS getSiteUrl() { console.log('SYNC MADE EASY: Setup => Checking Site Existance'); this.checkStatusSpinner = true; this.checkSiteExist = false; this.checkSiteAccess = false; this.remoteSiteExist = false; this.authenticationRemoteSite = false; this.certificateExist = false; this.noRefreshToken = false; getSiteURLs().then((result) => { if (result != null) { if (result == 'No access to site.') { this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.isSiteSupportedOrg = false; this.allSettingCompleted = true; this.disableCertificate = false; console.log('SITE NOT SUPPORTED'); // this.getSetupAccessCheck(); this.getRefreshToken(); } else { console.log('SITE SUPPORTED'); this.allSettingCompleted = false; this.siteURLList = JSON.parse(result); this.checkSiteExist = true; this.siteOptions = JSON.parse(result).map((element) => { return { label: element.siteUrl, value: element.siteguestid }; }); this.siteValue = this.siteURLList[0].siteguestid; console.log('@@@ this.siteValue : ' + this.siteValue); this.selectedSiteId = this.siteURLList[0].Id; this.siteConfiguration(); } } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.checkSiteExist = false; this.checkSiteAccess = false; } }).catch((error) => { console.log('@@@ This is the Error Occured in site url ', JSON.stringify(error)); this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_in_site_url); }); } // RETURNING DOMAIN URL FOR REMOTE SITE SETTING returnDomain() { returnDomainUrl().then((result) => { this.CreateRemotesiteSettingUrl = result; this.orgDomainUrl = result; }).catch((error) => { this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_in_domain_url); }); } // CHECK IF SITE EXISTS IN CONFIURATION siteConfiguration() { console.log('SYNC MADE EASY: Setup => Checking Site Configuration'); CheckSitefromConfiguration().then((result) => { if (result == true || (result && result.includes && result.includes("http"))) { // URL already saved before — use that site this.selectedSite = result; for (let site of this.siteURLList) { if (site.siteUrl == this.selectedSite) { this.selectedSiteId = site.id; this.siteValue = site.siteguestid; } } this.urlSavedSuccess = true; this.siteClassAndPageAccess(); } else { // ✅ FIX: URL not saved yet. Try saving now if access is granted. // Default to first site and run access check → save flow if (this.siteURLList && this.siteURLList.length > 0) { this.siteValue = this.siteURLList[0].siteguestid; this.siteurl = this.siteURLList[0].siteUrl; this.siteName = this.siteURLList[0].name; this.selectedSiteId = this.siteURLList[0].id; this.siteClassAndPageAccess(); // This will check access + save URL } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); } } }).catch((error) => { this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_while_checking_site_configuration); }); } // THIS METHOD CHECKS THE ACCESS TO THE SITE FOR THE SPECIFIC USER siteClassAndPageAccess() { console.log('SYNC MADE EASY: Setup => Checking Class Access'); for (let site of this.siteURLList) { if (site.siteguestid == this.siteValue) { this.siteurl = site.siteUrl; this.siteName = site.name; this.selectedSiteId = site.id; } } checkSiteClassandPageAccess({ Guestuserid: this.siteValue }) .then((result) => { console.log('@@@ checkSiteClassandPageAccess : ' + result); if (result == true) { this.checkSiteAccess = true; // ✅ Save URL to BOTH Configuration objects in parallel return Promise.all([ savesiteurlconfig({ siteurl: this.siteurl }), saveSiteUrlOnHicConfig({ siteurl: this.siteurl }) ]); } else { this.disableDomainRemoteSite = true; this.disableAuthenticateRemoteSite = true; this.disableCertificate = true; this.checkSiteAccess = false; this.urlSavedSuccess = false; this.siteSavingNotification = `${this.siteName}${this.label.site_currently_does_not_have_access_to_the_class} 'HIC_ShopifySync.AuthenticateAllApexClass' ${this.label.Please_activate_the_site_or_grant_access}`; this.checkStatusSpinner = false; this.closeSpinnerHandler(); return null; } }) .then((saveResults) => { if (saveResults === null) return; const [customSettingSave, hicConfigSave] = saveResults; console.log('@@@ savesiteurlconfig (Configuration__c): ' + customSettingSave); console.log('@@@ saveSiteUrlOnHicConfig (HIC_Configuration__c): ' + hicConfigSave); // Consider success if at least one saved (Configuration__c is the critical one for auth) if (customSettingSave == true) { console.log('@@@ ✅ Site URL saved on both Configuration objects'); this.urlSavedSuccess = true; this.checkSiteExist = true; this.disableDomainRemoteSite = false; this.disableAuthenticateRemoteSite = false; this.siteSavingNotification = "This site has been saved Successfully!"; this.closeSiteModel(); this.getRefreshToken(); } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); } }) .catch((error) => { console.log('@@@ Error: ' + JSON.stringify(error)); this.checkStatusSpinner = false; this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_while_checking_site_page_access); }); } // THIS METHOD IS USED TO SAVE THE SITE CONFIGURATION USER HAS SELECTED saveSiteUrlConfiguration() { savesiteurlconfig({ siteurl: this.siteurl }) .then((result) => { if (result == true) { this.getRefreshToken(); this.disableDomainRemoteSite = false; this.disableAuthenticateRemoteSite = false; this.checkSiteAccess = true; this.checkSiteExist = true; this.urlSavedSuccess = true; this.siteSavingNotification = "This site has been saved Successfully!"; this.closeSiteModel(); } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); } }) .catch((error) => { this.checkStatusSpinner = false; this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_while_saving_site_url); }); } //Get Refresh token getRefreshToken() { console.log('SYNC MADE EASY: Setup => Checking tokens'); refreshTokenCheck().then((result) => { if (result === 'No refresh token found') { console.log('SYNC MADE EASY: Setup => No Refresh Token Found'); this.noRefreshToken = true; this.runPreAuthChecks(); } else { this.refreshTokenValue = result; if (result === 'No access token found') { console.log('SYNC MADE EASY: Setup => No Access Token Found'); this.noAccessToken = true; this.noRefreshToken = true; this.runPreAuthChecks(); } else { console.log('SYNC MADE EASY: Setup => Access Token Found'); this.noRefreshToken = false; this.accessToken = result; this.shopifyCertificateSite(); } } }).catch((error) => { this.checkStatusSpinner = false; this.showNotification(this.label.Error, 'Error', this.label.Some_error_occurred_in_refresh_token); }); } //THIS METHOD IS USED TO CHECK IF THE USER HAS ACCESS TO SHOPIFY CLASS AND PAGE, REMOTE SITE AND CERTIFICATE getSetupAccessCheck() { console.log('SYNC MADE EASY: Setup => Checking Final Access ' + this.siteValue + ' this is acccess Token :: ' + this.accessToken); setupAccessCheck({ guestUserId: this.siteValue, accessToken: this.accessToken }).then((result) => { console.log('@@@ setupAccessCheck : ' + JSON.stringify(result)); if (result.classAndPageAccess) { this.disableDomainRemoteSite = false; this.disableAuthenticateRemoteSite = false; this.disableCertificate = true; if (result.remoteSite) { this.remoteSiteExist = true; this.authenticationRemoteSite = true; this.disableDomainRemoteSite = true; this.disableAuthenticateRemoteSite = true; this.disableCertificate = false; if (result.certificate) { console.log('@@@ result.certificate', result.certificate); this.disableCertificate = true; this.remoteSiteExist = true; this.authenticationRemoteSite = true; this.certificateExist = true; this.isCertificate = false; this.unlockBodyScroll(); this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.isStatusChecked = true; this.showCheckStatusMessage = false; console.log('@@@ this.storeName : ' + this.storeName); // Auto-advance to Step 2 ONLY when this verification run came from // the automatic page-load/refresh check (this._isAutoLoadCheck), // and only the first time per page load (hasAutoAdvancedOnRefresh). // A manual "Check Status" button click (handleCheckStatusClick sets // _isAutoLoadCheck = false) never auto-advances — it just marks the // step Verified and stays put; moving on then requires an explicit // Next click. if (this._isAutoLoadCheck && !hasAutoAdvancedOnRefresh) { hasAutoAdvancedOnRefresh = true; this._isAutoLoadCheck = false; console.log('SYNC MADE EASY: Initial refresh with setup verified — auto-advancing to Step 2'); this.goNextPage(); } else { hasAutoAdvancedOnRefresh = true; } } else { console.log('@@@ inside else'); this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.disableCertificate = false; this.certificateExist = false; this.isCertificate = true; this.lockBodyScroll(); this.certificateHeaderMsg = this.label.Create_certificate; } } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.isRemoteSite = true; this.remoteSiteExist = false; this.authenticationRemoteSite = false; this.remoteSiteHeaderMsg = this.label.Create_Remote_site; this.disableDomainRemoteSite = false; this.disableAuthenticateRemoteSite = false; } } else { this.checkStatusSpinner = false; this.closeSpinnerHandler(); this.disableDomainRemoteSite = true; this.disableAuthenticateRemoteSite = true; this.disableCertificate = true; this.urlSavedSuccess = false; } }).catch((error) => { console.error('SYNC MADE EASY: Setup => setupAccessCheck failed: ' + JSON.stringify(error)); this.checkStatusSpinner = false; this.closeSpinnerHandler(); }) } @api goNextPage() { console.log('@@@ isSiteSupportedOrg : ' + this.isSiteSupportedOrg); console.log('@@@ urlSavedSuccess : ' + this.urlSavedSuccess); console.log('@@@ remoteSiteExist : ' + this.remoteSiteExist); console.log('@@@ authenticationRemoteSite : ' + this.authenticationRemoteSite); console.log('@@@ certificateExist : ' + this.certificateExist); if (!this.checkSiteExist && this.isSiteSupportedOrg) { this.showNotification(this.label.Error, 'Error', this.label.Please_create_new_site_or_activate_existing_site); } else if (!this.urlSavedSuccess && this.isSiteSupportedOrg) { this.showNotification(this.label.Error, 'Error', this.label.Please_configure_site); } else if (!this.remoteSiteExist) { this.showNotification(this.label.Error, 'Error', this.label.Please_create_domain_Url_remote_site_setting); } else if (!this.authenticationRemoteSite) { this.showNotification(this.label.Error, 'Error', this.label.Please_create_authenticate_Url_remote_site_setting); } else if (!this.certificateExist) { this.showNotification(this.label.Error, 'Error', this.label.Please_create_self_signed_certificate); } else if (this.isStatusChecked) { const selectStore = new CustomEvent('save', { detail: 'Saved' }); this.dispatchEvent(selectStore); } // const selectStore = new CustomEvent('save', { // detail: 'Saved' // }); // this.dispatchEvent(selectStore); } // THIS METHOD IS CALLED FROM THE HOME COMPONENT ON SAVE EVENT AND USED TO CHECK IF THE USER HAS ACCESS TO SITE AND REMOTE SITE SETTING AND CERTIFICATE @api async shopifyCertificateSite() { for (let site of this.siteURLList) { if (site.siteguestid == this.siteValue) { this.siteurl = site.siteUrl; this.siteName = site.name; this.selectedSiteId = site.id; } } this.getSetupAccessCheck(); } // METHOD USED TO REDIRECT TO REMOTE SITE SETTING PAGE WITH PRE POPULATED VALUES TO CREATE createRemoteSiteSetting() { if ( this.RemotesiteUrl != null && this.RemotesiteUrl != "" && this.RemotesiteUrl !== undefined ) { this.openSetupPage( "/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_Shopifymadeeasy_Site_URl%26EndpointUrl=" + this.RemotesiteUrl ); } else { this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_Shopifymadeeasy_Site_URl"); } } // METHOD USED TO REDIRECT TO REMOTE SITE SETTING PAGE WITH PRE POPULATED VALUES TO CREATE FOR MIDDLEWARE serverRemoteSite() { var serverUrlLink = 'https://sfapps.syncmadeasy.com'; this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_SME_RealTimeUrl%26EndpointUrl=" + serverUrlLink); } // METHOD USED TO REDIRECT TO REMOTE SITE SETTING PAGE WITH PRE POPULATED VALUES TO CREATE domainUrlRemoteSiteSetting() { if ( this.CreateRemotesiteSettingUrl != null && this.CreateRemotesiteSettingUrl != "" && this.CreateRemotesiteSettingUrl !== undefined ) { this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_Shopifymadeeasy_Site_URl%26EndpointUrl=" + this.CreateRemotesiteSettingUrl); } else { this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_Shopifymadeeasy_Site_URl"); } } // METHOD USED TO REDIRECT TO REMOTE SITE SETTING PAGE WITH PRE POPULATED VALUES TO CREATE authenticateUrlRemoteSiteSetting() { if ( this.authenticationUrl != null && this.authenticationUrl != "" && this.authenticationUrl !== undefined ) { this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_SME_AuthenticateUrl%26EndpointUrl=" + this.authenticationUrl); } else { this.openSetupPage("/lightning/setup/SecurityRemoteProxy/page?address=/0rp/e?SiteName=HIC_SME_AuthenticateUrl"); } } // METHOD USED TO OPEN THE MODEL TO SELECT SITE AND SAVE IT TO CUSTOM SETTINGS openSiteModel() { // If the org has no site to choose from, don't open the popup — send the // user straight to the Salesforce Sites page so they can create/activate // one. The popup only opens when a site already exists to select. const hasSiteToSelect = this.hasSites && Array.isArray(this.siteOptions) && this.siteOptions.length > 0; if (!hasSiteToSelect) { this.openSetupPage("/lightning/setup/CustomDomain/home"); return; } this.siteModel = true; if (this.urlSavedSuccess == true) { this.siteSavingNotification = this.label.You_Can_Change_Site; } else { this.siteSavingNotification = this.label.Please_Setup_Site; } } // METHOD USED TO CLOSE THE SITE MODEL closeSiteModel() { this.siteModel = false; } // METHOD USED TO TRACK THE CHANGE IN SELECTION OF THE SITE handleSiteChange(event) { this.siteValue = event.detail.value; } // METHOD USED TO CLOSE THE REMOTE SITE SETTING MODAL closeRemoteSiteModel() { this.isRemoteSite = false; } // METHOD USED TO REDIRECT TO THE CRETIFICATE CREATION SITE ReDirectCertificateSetting() { this.isCertificate = false; this.unlockBodyScroll(); this.openSetupPage(this.CertificateCreateLink); } // METHOD USED TO CLOSE THE CERTIFICATE WARNING MODAL closeCertificateModal() { this.isCertificate = false; this.unlockBodyScroll(); } // THIS METHOD IS USED TO CALL THE VF PAGE FOR LWC APP TO INITIALIZE THE PROCESS openInitialize() { if (this.isSiteSupportedOrg) { console.log('@@@ Initalize SITE SUPPORTED'); var authenticateurl = '/apex/hic_shopifysync__SalesforceAuthentication?viewmode=' + this.viewMode + '&islwcapp=true'; window.open(authenticateurl, '_top'); } else if (!this.isSiteSupportedOrg) { console.log('@@@ Initalize SITE NOT SUPPORTED'); this.redirectToSalesforce(); } // var authenticateurl = '/apex/hic_shopifysync__SalesforceAuthentication?viewmode=' + this.viewMode + '&islwcapp=true'; // window.open(authenticateurl, '_top'); } // This method is used to redirect to site for Public Access redirectToSite() { // this.siteValue for (let site of this.siteURLList) { if (site.siteguestid == this.siteValue) { this.siteurl = site.siteUrl; this.siteName = site.name; this.selectedSiteId = site.id; } } this.openSetupPage('/lightning/setup/CustomDomain/page?address=/' + this.selectedSiteId); } // Open a Salesforce setup page in a new tab, safely for installed (managed-package) orgs. // A relative URL passed to window.open resolves against the sandboxed Locker base // (b.static.lightning.force.com) in subscriber orgs and breaks ("Invalid url: https://"). // Prefixing the real org My Domain removes that ambiguity and keeps the new-tab UX. openSetupPage(relativeUrl) { if (this.orgDomainUrl) { const absoluteUrl = this.orgDomainUrl + relativeUrl; console.log('SYNC MADE EASY: Setup => opening setup page: ' + absoluteUrl); window.open(absoluteUrl, '_blank'); } else { // Org domain not loaded yet — fall back to the platform navigation service (same tab). console.warn('SYNC MADE EASY: Setup => org domain not loaded, using NavigationMixin fallback'); this[NavigationMixin.Navigate]({ type: 'standard__webPage', attributes: { url: relativeUrl } }); } } //This Method is used to close spinner closeSpinnerHandler() { this.isLoading = false; const stopSpinner = new CustomEvent('closespinner', { detail: 'Stop Spinner' }); this.dispatchEvent(stopSpinner); } //This Method is used to show spinner showSpinnerHandler() { this.isLoading = true; const startSpinner = new CustomEvent('showspinner', { detail: 'Start Spinner' }); this.dispatchEvent(startSpinner); } //Helps to Show a Toast Message // Custom in-component toast instead of ShowToastEvent: success shows the // brand green→teal gradient, error shows red, matching the reference. Same // (title, variant, message) signature and callers as before — variant is // matched case-insensitively ('Success'/'Error'/'Warning' all work). 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++); const toast = { id, title, message, cssClass: 'sw-toast sw-toast-' + kind, iconLabel: kind === 'error' ? '⚠' : kind === 'success' ? '✓' : kind === 'warning' ? '!' : 'ℹ' }; this.toasts = [...this.toasts, toast]; // Auto-dismiss non-error toasts after 4s; errors stay until closed. if (kind !== 'error') { // eslint-disable-next-line @lwc/lwc/no-async-operation setTimeout(() => this._dismissToast(id), 4000); } this.closeSpinnerHandler(); } _dismissToast(id) { this.toasts = this.toasts.filter((t) => t.id !== id); } handleToastClose(event) { this._dismissToast(event.currentTarget.dataset.id); } get isNextDisabled(){ // SITE CHECKBOXES DO NOT APPLY TO NON SITE SUPPORTED ORGS if (!this.isSiteSupportedOrg) { return !(this.remoteSiteExist && this.authenticationRemoteSite && this.certificateExist); } return !(this.checkSiteExist && this.urlSavedSuccess && this.remoteSiteExist && this.authenticationRemoteSite && this.certificateExist) } // SHOW INITIALIZE BUTTON IN PLACE OF VERIFY WHEN REFRESH TOKEN IS EMPTY (FIRST TIME / RE-AUTH) get showInitializeButton() { return this.noRefreshToken; } // HIDE CHECK STATUS ONLY IN THE PRE-AUTH CASE — NO REFRESH TOKEN YET, BUT ALL // 4 CHECKBOXES ARE VERIFIED, SO INITIALIZE IS THE CORRECT NEXT ACTION (NOT // CHECK STATUS: clicking Check Status here skips straight past "Ready to // verify" to "Verified" without going through the real OAuth/Initialize // chain, since setupAccessCheck can confirm site/remote-site/certificate // without a token). Once isStatusChecked is real/true, Check Status is // NEVER hidden anymore — it stays visible as "Verified ✓" alongside // Verify & Next (see checkButtonClass), it just no longer disappears. get hideCheckStatus() { return !this.isStatusChecked && this.noRefreshToken && !this.isInitializeDisabled; } // INITIALIZE ENABLED ONLY WHEN ALL 4 CHECKBOXES ARE VERIFIED (SAME GATING AS VERIFY) get isInitializeDisabled() { if (this.isSiteSupportedOrg) { return this.isNextDisabled; } // NON SITE SUPPORTED ORG HAS NO SITE CHECKBOX return !(this.remoteSiteExist && this.authenticationRemoteSite && this.certificateExist); } // PRE-AUTH (TOKENLESS) VERIFICATION OF RSS + CERTIFICATE, RUNS ON CHECK STATUS WHEN NO TOKEN EXISTS runPreAuthChecks() { preAuthSetupCheck().then((result) => { this.remoteSiteExist = result.domainRemoteSite === true; this.authenticationRemoteSite = result.authRemoteSite === true; this.certificateExist = result.certificate === true; this.disableDomainRemoteSite = this.remoteSiteExist; this.disableAuthenticateRemoteSite = this.authenticationRemoteSite; this.disableCertificate = this.certificateExist; this.checkStatusSpinner = false; this.closeSpinnerHandler(); }).catch((error) => { console.log('@@@ preAuthSetupCheck error : ' + JSON.stringify(error)); this.checkStatusSpinner = false; this.closeSpinnerHandler(); }); } // INITIALIZE + VERIFY: RUNS OAUTH FLOW; ON RETURN THE COMPONENT RELOADS AND // connectedCallback => getSiteUrl() RE-RUNS THE FULL VERIFICATION CHAIN AUTOMATICALLY handleInitializeVerify() { this.openInitialize(); } redirectToSalesforce() { console.log('>>> URL TO INITIALISE '); let clientId; let redirectUrl; if (this.authenticationUrl.includes('login')) { clientId = '3MVG9G9pzCUSkzZv4NsQV0kOphm0Il0eDJK5e0YicZLcCZKQcDHS0FORFMYHW8LzqJWPeMxzGUxeyshwEBOin' redirectUrl = 'https://login.salesforce.com/apex/HIC_ShopifySync__SalesforceAuthentication'; } else { clientId = '3MVG9G9pzCUSkzZv4NsQV0kOphuwhNCDbV2xJ4wazQQkD_nyOa73sUwa0Z0DtHz1t5woA9d9AVNwcHvqbQy45'; redirectUrl = 'https://test.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() { // this.showSpinnerHandler(); // runPostInstallScript().then((result) => { // if (result === 'Success') { // console.log('@@@ Post install script run successfully.'); // this.showNotification('Success', 'Success', 'Field Mapping of Default store has been reset.'); // this.closeSpinnerHandler(); // } // }).catch((error) => { // console.log('@@@ Post install script error.' + error); // this.closeSpinnerHandler(); // }) // } lockBodyScroll() { // document.body / window are owned by the host page, not this component. // Lightning Locker blocks that access for managed-package LWC in subscriber // orgs and throws (surfaced as the opaque "aura_prod.js Script error. null"). // Scroll-locking is purely cosmetic, so never let it break the setup flow. try { document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.width = '100%'; this.scrollY = window.scrollY; } catch (e) { console.warn('SYNC MADE EASY: lockBodyScroll skipped (restricted DOM): ' + e); } } // UNLOCK BODY SCROLL WHEN MODAL CLOSES unlockBodyScroll() { try { document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.width = ''; if (typeof this.scrollY === 'number') { window.scrollTo(0, this.scrollY); } } catch (e) { console.warn('SYNC MADE EASY: unlockBodyScroll skipped (restricted DOM): ' + e); } } // ========================================================================== // NEW — presentation-only getters that drive the reference-mockup template. // These do not add any new business logic; they simply project the existing // state above into the checklist-card / status-pill / modal look. // ========================================================================== get tasks() { const list = []; if (this.isSiteSupportedOrg) { list.push({ key: 'site', title: 'Activate a Salesforce site', desc: this.checkSiteExist ? 'Select and confirm the Salesforce site used to authorize the connection.' : 'No active site found in your org. Create a new site or activate an existing one.', done: this.urlSavedSuccess, clickable: !this.urlSavedSuccess, handler: 'site' }); } list.push({ key: 'domain', title: 'Domain URL — Remote Site Setting', desc: 'Allow Salesforce to send requests to your domain URL.', done: this.remoteSiteExist, clickable: !this.disableDomainRemoteSite, handler: 'domain' }); list.push({ key: 'auth', title: 'Authenticate URL — Remote Site Setting', desc: 'Whitelist the authentication endpoint used during login.', done: this.authenticationRemoteSite, clickable: !this.disableAuthenticateRemoteSite, handler: 'auth' }); list.push({ key: 'cert', title: 'Self-signed certificate', desc: 'Generate the certificate used to sign secure requests.', done: this.certificateExist, clickable: !this.disableCertificate, handler: 'cert' }); return list.map((t, i) => ({ ...t, index: i, // "Open again" removed everywhere: the action link only renders while // the step is still pending (not done). showAction: !t.done, actionLabel: t.done ? 'Open again' : 'Click here to configure', tag: t.done ? 'Done' : 'Required', rowClass: 'task-row', boxClass: t.done ? 'task-box task-box_done' : 'task-box', tagClass: t.done ? 'task-tag task-tag_done' : 'task-tag task-tag_required', // Link always renders (matches setup.html); it's only visually/ // functionally disabled via pointer-events when clickable is false. actionClass: t.clickable ? 'task-action' : 'task-action task-action_disabled' })); } get doneCount() { return this.tasks.filter((t) => t.done).length; } get totalTasks() { return this.tasks.length; } get verified() { return this.isStatusChecked || this.hideCheckStatus; } get statusLabel() { if (this.verified) return 'Verified'; return this.doneCount === this.totalTasks ? 'Ready to verify' : 'In progress'; } get statusPillClass() { if (this.verified) return 'status-pill status-pill_verified'; return this.doneCount === this.totalTasks ? 'status-pill status-pill_ready' : 'status-pill status-pill_progress'; } get checkLabel() { if (this.checkStatusSpinner) return 'Checking…'; return this.verified ? 'Verified ✓' : 'Check status'; } get checkIconClass() { return this.checkStatusSpinner ? 'check-icon check-icon_spin' : 'check-icon'; } // Check Status button now stays visible (alongside Verify & Next) once // verified instead of disappearing — matches the reference mockup, where // both "Verified ✓" and "Verify & Next" show together until the user // clicks Verify & Next. Only styling/disabled state changes here; the // real isStatusChecked/verified logic driving it is untouched. get checkButtonClass() { return this.verified ? 'btn-check btn-check_done' : 'btn-check'; } get nextButtonClass() { return !this.isNextDisabled ? 'btn-next btn-next_active' : 'btn-next'; } get hasSites() { return this.isSiteSupportedOrg; } get noSites() { return !this.isSiteSupportedOrg; } get siteChosen() { return this.isSiteSupportedOrg && !!this.siteValue && this.urlSavedSuccess; } get successButtonDisabled() { return !this.siteValue; } get successButtonClass() { return this.successButtonDisabled ? 'btn-success btn-success_disabled' : 'btn-success'; } // Single click dispatcher for the checklist rows — routes to the // existing, unmodified handler methods above based on which row was clicked. handleTaskAction(event) { event.stopPropagation(); const handler = event.currentTarget.dataset.handler; switch (handler) { case 'site': this.openSiteModel(); break; case 'domain': this.domainUrlRemoteSiteSetting(); break; case 'auth': this.authenticateUrlRemoteSiteSetting(); break; case 'cert': this.ReDirectCertificateSetting(); break; default: break; } } handleModalContentClick(event) { event.stopPropagation(); } }