From cff2faeb7a9c527bf7c97624aa1b2852aa7d3dd7 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Thu, 25 Sep 2025 13:43:44 -0300 Subject: [PATCH 01/11] refactor the tour flow to ensure a default class --- src/dashboard/Data/Browser/Browser.react.js | 76 ++++++++++++++++++--- 1 file changed, 67 insertions(+), 9 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 16c874da4c..cb182052ee 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -243,7 +243,6 @@ class Browser extends DashboardView { if (!currentApp.preventSchemaEdits) { this.action = new SidebarAction( Add class, this.showCreateClass.bind(this)); } - this.props.schema.dispatch(ActionTypes.FETCH).then(() => { this.handleFetchedSchema(); !this.props.params.className && this.redirectToFirstClass(this.props.schema.data.get('classes')); @@ -267,9 +266,33 @@ class Browser extends DashboardView { window.addEventListener('resize', this.windowResizeHandler); } - componentDidMount() { + async componentDidMount() { this.addLocation(this.props.params.appId); + + try { + await this.props.schema.dispatch(ActionTypes.FETCH); + this.handleFetchedSchema(); + const classes = this.props.schema.data.get('classes'); + const currentUser = AccountManager.currentUser(); + + if (currentUser.playDatabaseBrowserTutorial && (!classes || classes.size === 0)) { + const className = 'B4aVehicle'; + + await this.createClass(className, false); + + this.addColumn({ type: 'String', name: 'name', required: true }); + this.addColumn({ type: 'Number', name: 'price', required: true }); + this.addColumn({ type: 'String', name: 'color', required: false }); + } + + if (!this.props.params.className) { + this.redirectToFirstClass(this.props.schema.data.get('classes')); + } + } catch (fetchError) { + console.error('Erro ao buscar schema:', fetchError); + } } + componentWillUnmount() { this.removeLocation(); @@ -315,8 +338,19 @@ class Browser extends DashboardView { this.prefetchData(nextProps, nextContext); } } - + getTourConfig() { + const updateState = (saveObject) => { + + this.setState(prev => { + const newData = prev.data ? [saveObject, ...prev.data] : [saveObject]; + return { + data: newData, + counts: { ...prev.counts, B4aVehicle: (prev.counts?.B4aVehicle || 0) + 1 }, + clp: { ...prev.clp, B4aVehicle: {} } + }; + }); + } const createClassCode = `
const B4aVehicle = Parse.Object.extend('B4aVehicle');
@@ -412,6 +446,9 @@ class Browser extends DashboardView { throw new Error('Component not ready'); }; + let vehicleRowCreated = false; + + return { steps, onBeforeStart: () => { @@ -430,7 +467,6 @@ class Browser extends DashboardView { }, onBeforeChange: function(targetElement) { const introItems = this._introItems; - // Fires event if it's not a forced transition if (!this._forcedStep) { amplitudeLogEvent(`On Database Browser Tour Step ${introItems[this._currentStep].eventId}`) @@ -461,7 +497,7 @@ class Browser extends DashboardView { break; case 3: { - if (!getCustomVehicleClassLink() && !unexpectedErrorThrown) { + if (!getCustomVehicleClassLink() && !unexpectedErrorThrown && !vehicleRowCreated) { schema.dispatch(ActionTypes.CREATE_CLASS, { className: 'B4aVehicle', fields: { @@ -469,14 +505,31 @@ class Browser extends DashboardView { price: { type: 'Number' }, color: { type: 'String' }, } - }).then(() => { - return context.apiRequest('POST', '/classes/B4aVehicle', { name: 'Corolla', price: 19499, color: 'black' }, { useMasterKey: true }); + }).catch(e => { + if (e.code === 103 && e.message.includes('already exists')) { + console.log('Classe já existe, continuando para criar objeto...'); + return; + } + throw e; + }).then(async () => { + vehicleRowCreated = true; + + const Vehicle = Parse.Object.extend('B4aVehicle'); + const vehicle = new Vehicle(); + + vehicle.set('name', 'Corolla'); + vehicle.set('price', 19499); + vehicle.set('color', 'black'); + + const savedObject = await vehicle.save(null, { useMasterKey: true }); + return savedObject + }).then((savedObject) => { + updateState(savedObject) }).then(() => { introItems[3].element = getCustomVehicleClassLink(); this.nextStep(); }).catch(e => { if (!unexpectedErrorThrown) { - console.log(introItems); introItems.splice(3, 2); for (let i = 3; i < introItems.length; i++) { introItems[i].step -= 2; @@ -748,7 +801,7 @@ class Browser extends DashboardView { if (semver.lte(this.context.serverInfo.parseServerVersion, '3.1.1')) { clp = {}; } - this.props.schema.dispatch(ActionTypes.CREATE_CLASS, { className, clp }).then(() => { + return this.props.schema.dispatch(ActionTypes.CREATE_CLASS, { className, clp }).then(() => { this.state.counts[className] = 0; this.state.clp[className] = clp; this.props.navigate(generatePath(this.context, 'browser/' + className)); @@ -760,6 +813,11 @@ class Browser extends DashboardView { let errorDeletingNote = 'Internal server error' if (error.code === 403) {errorDeletingNote = error.message;} + if (error.code === 103 && error.message.includes(`already exists`)) { + console.log(`Class ${className} already exists`); + return; + } + this.showNote(errorDeletingNote, true); }).finally(() => { this.setState({ showCreateClassDialog: false }); From e1cb3fed9976ebd22b28f1fe759fe3d31311b569 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Thu, 25 Sep 2025 13:55:13 -0300 Subject: [PATCH 02/11] fix console.log --- src/dashboard/Data/Browser/Browser.react.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index cb182052ee..fe02a4cf10 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -289,7 +289,7 @@ class Browser extends DashboardView { this.redirectToFirstClass(this.props.schema.data.get('classes')); } } catch (fetchError) { - console.error('Erro ao buscar schema:', fetchError); + console.error('Error fetching schema:', fetchError); } } @@ -507,7 +507,7 @@ class Browser extends DashboardView { } }).catch(e => { if (e.code === 103 && e.message.includes('already exists')) { - console.log('Classe já existe, continuando para criar objeto...'); + console.log('Class already exists, continue creating the object...'); return; } throw e; From 721564ad814ea69814e2a85b7207ca5b066ed2f1 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Thu, 25 Sep 2025 14:49:57 -0300 Subject: [PATCH 03/11] Fix to highlight api reference --- src/dashboard/Data/Browser/Browser.react.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index fe02a4cf10..a38fbe4bc3 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -384,7 +384,7 @@ class Browser extends DashboardView { }, { eventId: 'Custom Class and Object Creation', - element: () => document.querySelectorAll('.section')[2], + element: () => document.querySelectorAll('.section')[3], intro: `It’s very simple to save data on Back4App from your front-end.

On the API Reference section, you can find the auto-generated code below that creates a class and persist data on it.
${createClassCode} From 9c899adaf7d8468964e6d3500b1e77bd58ecc4b6 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Fri, 3 Oct 2025 09:41:29 -0300 Subject: [PATCH 04/11] fix tour flow --- src/dashboard/Data/Browser/Browser.react.js | 127 ++++++++------------ src/stylesheets/introjs.css | 11 +- 2 files changed, 57 insertions(+), 81 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index a38fbe4bc3..bd98498993 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -226,7 +226,23 @@ class Browser extends DashboardView { getFooterMenuButtons() { return this.state.renderFooterMenu || this.state.showTour ? [ - this.setState({ showTour: true })}>Play intro + { + this.setState({ showTour: true }) + const classes = this.props.schema.data.get('classes'); + const className = 'B4aVehicle'; + const hasClass = classes.has(className); + + if(classes.size == 0 || !hasClass){ + + await this.createClass(className, false); + + this.addColumn({ type: 'String', name: 'name', required: true }); + this.addColumn({ type: 'Number', name: 'price', required: true }); + this.addColumn({ type: 'String', name: 'color', required: false }); + } + }}>Play intro ] : null; } @@ -268,29 +284,6 @@ class Browser extends DashboardView { async componentDidMount() { this.addLocation(this.props.params.appId); - - try { - await this.props.schema.dispatch(ActionTypes.FETCH); - this.handleFetchedSchema(); - const classes = this.props.schema.data.get('classes'); - const currentUser = AccountManager.currentUser(); - - if (currentUser.playDatabaseBrowserTutorial && (!classes || classes.size === 0)) { - const className = 'B4aVehicle'; - - await this.createClass(className, false); - - this.addColumn({ type: 'String', name: 'name', required: true }); - this.addColumn({ type: 'Number', name: 'price', required: true }); - this.addColumn({ type: 'String', name: 'color', required: false }); - } - - if (!this.props.params.className) { - this.redirectToFirstClass(this.props.schema.data.get('classes')); - } - } catch (fetchError) { - console.error('Error fetching schema:', fetchError); - } } @@ -394,7 +387,7 @@ class Browser extends DashboardView { }, { eventId: 'Custom Class Link', - element: () => document.querySelector('.class_list [title="B4aVehicle"]') || document.querySelector('.class_list'), + element: () => document.querySelector('#section_contents a[title="B4aVehicle"]') || document.querySelector('.class_list'), intro: 'This is the new B4aVehicle class just created!', position: 'right' }, @@ -433,7 +426,7 @@ class Browser extends DashboardView { } const getCustomVehicleClassLink = () => { - return document.querySelector('[class^=class_list] [title="B4aVehicle"]'); + return document.querySelector('#section_contents a[title="B4aVehicle"]'); }; const getNextComponentReadyPromise = async conditionFn => { @@ -446,7 +439,9 @@ class Browser extends DashboardView { throw new Error('Component not ready'); }; - let vehicleRowCreated = false; + let vehicleRowCreated = false; + const reactProps = this.props; + const reactContext = this.context; return { @@ -497,36 +492,19 @@ class Browser extends DashboardView { break; case 3: { - if (!getCustomVehicleClassLink() && !unexpectedErrorThrown && !vehicleRowCreated) { - schema.dispatch(ActionTypes.CREATE_CLASS, { - className: 'B4aVehicle', - fields: { - name: { type: 'String' }, - price: { type: 'Number' }, - color: { type: 'String' }, - } - }).catch(e => { - if (e.code === 103 && e.message.includes('already exists')) { - console.log('Class already exists, continue creating the object...'); - return; - } - throw e; - }).then(async () => { - vehicleRowCreated = true; - - const Vehicle = Parse.Object.extend('B4aVehicle'); - const vehicle = new Vehicle(); - - vehicle.set('name', 'Corolla'); - vehicle.set('price', 19499); - vehicle.set('color', 'black'); - - const savedObject = await vehicle.save(null, { useMasterKey: true }); - return savedObject - }).then((savedObject) => { + const Vehicle = Parse.Object.extend('B4aVehicle'); + const vehicle = new Vehicle(); + + vehicle.set('name', 'Corolla'); + vehicle.set('price', 19499); + vehicle.set('color', 'black'); + if (getCustomVehicleClassLink() && !vehicleRowCreated) { + vehicleRowCreated = true; + vehicle.save(null, { useMasterKey: true }).then((savedObject) => { updateState(savedObject) }).then(() => { - introItems[3].element = getCustomVehicleClassLink(); + const vehicleLink = document.querySelector('#section_contents a[title="B4aVehicle"]'); + introItems[3].element = vehicleLink; this.nextStep(); }).catch(e => { if (!unexpectedErrorThrown) { @@ -540,6 +518,22 @@ class Browser extends DashboardView { this.nextStep(); }); return false; + } else if(!getCustomVehicleClassLink()){ + schema.dispatch(ActionTypes.CREATE_CLASS, { + className: 'B4aVehicle', + fields: { + name: { type: 'String' }, + price: { type: 'Number' }, + color: { type: 'String' }, + } + }).then(async () => { + vehicleRowCreated = true; + await vehicle.save(null, { useMasterKey: true }) + }).then(() => { + const vehicleLink = document.querySelector('#section_contents a[title="B4aVehicle"]'); + introItems[3].element = vehicleLink; + this.nextStep(); + }) } const nextButton = getNextButton(); nextButton.innerHTML = 'Next'; @@ -547,15 +541,13 @@ class Browser extends DashboardView { const numberLayer = document.querySelector('.introjs-helperNumberLayer'); numberLayer.style.marginLeft = 0; if (!unexpectedErrorThrown) { - this.props.navigate(generatePath(this.context, 'browser/B4aVehicle')); + reactProps.navigate(generatePath(reactContext, 'browser/B4aVehicle')); // history.push(this.context.generatePath('browser/B4aVehicle')); } } break; case 4: - if (!unexpectedErrorThrown) { - this._introItems[4].element = document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span'); - } + this._introItems[4].element = document.querySelector('#browser > div'); break; case 5: if(unexpectedErrorThrown) { @@ -584,22 +576,6 @@ class Browser extends DashboardView { targetElement.style.backgroundColor = 'inherit'; } break; - case 3: - if (!unexpectedErrorThrown) { - if (!document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')){ - // next row has not rendered yet - const nextButton = getNextButton(); - nextButton.innerHTML = `
`; - nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); - getNextComponentReadyPromise(() => document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')) - .then(() => { - nextButton.innerHTML = 'Next'; - nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); - }) - } - targetElement.style.backgroundColor = '#0e69a0'; - } - break; } }, onBeforeExit: function() { @@ -814,7 +790,6 @@ class Browser extends DashboardView { if (error.code === 403) {errorDeletingNote = error.message;} if (error.code === 103 && error.message.includes(`already exists`)) { - console.log(`Class ${className} already exists`); return; } diff --git a/src/stylesheets/introjs.css b/src/stylesheets/introjs.css index 223abde6f5..4bad379e41 100644 --- a/src/stylesheets/introjs.css +++ b/src/stylesheets/introjs.css @@ -54,8 +54,7 @@ tr.introjs-showElement > th { box-sizing: content-box; position: absolute; z-index: 9999998; - background-color: #FFF; - background-color: rgba(255,255,255,.9); + background-color: rgba(255, 255, 255, 0.300); border: 1px solid #777; border: 1px solid rgba(0,0,0,.5); border-radius: 4px; @@ -383,12 +382,14 @@ tr.introjs-showElement > th { color: darkgrey; } -@media only screen and (max-width: 1440px) { +/* For retina screens (MacBooks) */ +@media only screen and (max-width: 1728px) and (-webkit-min-device-pixel-ratio: 2), + only screen and (max-width: 1728px) and (min-resolution: 192dpi) { .tourThirdStepStyle { - top: -375px !important; + top: -100px !important; } .tourThirdStepStyle > div.introjs-arrow.left-bottom { - bottom: 95px !important; + bottom: 390px !important; } } \ No newline at end of file From 86ac48351d222e3eb2f54d6c88d9e2125f68c54d Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Mon, 6 Oct 2025 13:20:51 -0300 Subject: [PATCH 05/11] fix ui behaviors --- src/dashboard/Data/Browser/Browser.react.js | 125 +++++++++++++++----- src/stylesheets/introjs.css | 17 +++ 2 files changed, 114 insertions(+), 28 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index bd98498993..48409bf86f 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -61,6 +61,7 @@ import { withRouter } from 'lib/withRouter'; import Icon from 'components/Icon/Icon.react'; import { amplitudeLogEvent } from 'lib/amplitudeEvents'; import { pushGTMEvent } from 'lib/gtm.js' +import { get } from 'jquery'; const BROWSER_LAST_LOCATION = 'b4a_brower_last_location'; // The initial and max amount of rows fetched by lazy loading @@ -229,7 +230,6 @@ class Browser extends DashboardView { { - this.setState({ showTour: true }) const classes = this.props.schema.data.get('classes'); const className = 'B4aVehicle'; const hasClass = classes.has(className); @@ -242,6 +242,7 @@ class Browser extends DashboardView { this.addColumn({ type: 'Number', name: 'price', required: true }); this.addColumn({ type: 'String', name: 'color', required: false }); } + this.setState({ showTour: true }) }}>Play intro ] : null; } @@ -333,6 +334,11 @@ class Browser extends DashboardView { } getTourConfig() { + const blockKeys = (e) => { + e.preventDefault(); + e.stopPropagation(); + }; + const updateState = (saveObject) => { this.setState(prev => { @@ -373,7 +379,7 @@ class Browser extends DashboardView { eventId: 'Database Browser Section', element: () => document.querySelector('#section_contents > div > div'), intro: 'This is the Database Browser section where you can create classes and manage your data using this Dashboard.', - position: 'right' + position: 'right', }, { eventId: 'Custom Class and Object Creation', @@ -383,18 +389,18 @@ class Browser extends DashboardView { ${createClassCode}

Click on the Run button to execute this code.

`, position: 'right', - tooltipClass: 'tourThirdStepStyle' + tooltipClass: 'tourThirdStepStyle', }, { eventId: 'Custom Class Link', - element: () => document.querySelector('#section_contents a[title="B4aVehicle"]') || document.querySelector('.class_list'), - intro: 'This is the new B4aVehicle class just created!', + element: () => getCustomVehicleClassLink() || document.querySelector('.class_list'), + intro: 'This is the new B4aVehicle class we created!', position: 'right' }, { eventId: 'Custom Class Data Table', element: () => document.querySelector('#browser'), - intro: 'As you can see the B4aVehicle class already has its first data.', + intro: 'As you can see a row has been added in the new B4aVehicle class!', position: 'right' }, { @@ -439,6 +445,23 @@ class Browser extends DashboardView { throw new Error('Component not ready'); }; + async function createB4aVehicleClass() { + const Vehicle = Parse.Object.extend('B4aVehicle'); + const vehicle = new Vehicle(); + + vehicle.set('name', 'Corolla'); + vehicle.set('price', 19499); + vehicle.set('color', 'black'); + + try { + const savedObject = await vehicle.save(null, { useMasterKey: true }); + return savedObject; + } catch (error) { + console.error('Erro ao criar B4aVehicle:', error); + throw error; + } + } + let vehicleRowCreated = false; const reactProps = this.props; const reactContext = this.context; @@ -447,14 +470,13 @@ class Browser extends DashboardView { return { steps, onBeforeStart: () => { - // document.querySelector('#section_contents > div > div').style.backgroundColor = '#0e69a0'; + document.addEventListener('keydown', blockKeys, true); // document.querySelector('[class^="section_header"][href*="/apidocs"]').style.backgroundColor = "#0c5582"; if (className !== '_User' && className.indexOf('_') !== -1) { this.props.navigate(generatePath(this.context, 'browser/_User')); // history.push(this.context.generatePath('browser/_User')); } post('/tutorial', { databaseBrowser: true }); - // Updates the current logged user so that the tutorial won't be played // again when the user switches to another page user.playDatabaseBrowserTutorial = false; @@ -462,6 +484,10 @@ class Browser extends DashboardView { }, onBeforeChange: function(targetElement) { const introItems = this._introItems; + const prevEl = this._introItems[this._currentStep - 1]?.element; + if (prevEl) { + prevEl.style.backgroundColor = 'transparent'; + } // Fires event if it's not a forced transition if (!this._forcedStep) { amplitudeLogEvent(`On Database Browser Tour Step ${introItems[this._currentStep].eventId}`) @@ -492,20 +518,20 @@ class Browser extends DashboardView { break; case 3: { - const Vehicle = Parse.Object.extend('B4aVehicle'); - const vehicle = new Vehicle(); + const nextButton = getNextButton(); + if (nextButton) { + nextButton.innerHTML = `
`; + nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); + } - vehicle.set('name', 'Corolla'); - vehicle.set('price', 19499); - vehicle.set('color', 'black'); if (getCustomVehicleClassLink() && !vehicleRowCreated) { - vehicleRowCreated = true; - vehicle.save(null, { useMasterKey: true }).then((savedObject) => { + createB4aVehicleClass().then((savedObject) => { updateState(savedObject) + vehicleRowCreated = true; }).then(() => { - const vehicleLink = document.querySelector('#section_contents a[title="B4aVehicle"]'); - introItems[3].element = vehicleLink; - this.nextStep(); + introItems[3].element = getCustomVehicleClassLink(); + nextButton.innerHTML = 'Next'; + nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); }).catch(e => { if (!unexpectedErrorThrown) { introItems.splice(3, 2); @@ -515,9 +541,7 @@ class Browser extends DashboardView { unexpectedErrorThrown = true; } console.error(e); - this.nextStep(); }); - return false; } else if(!getCustomVehicleClassLink()){ schema.dispatch(ActionTypes.CREATE_CLASS, { className: 'B4aVehicle', @@ -527,16 +551,26 @@ class Browser extends DashboardView { color: { type: 'String' }, } }).then(async () => { - vehicleRowCreated = true; - await vehicle.save(null, { useMasterKey: true }) - }).then(() => { - const vehicleLink = document.querySelector('#section_contents a[title="B4aVehicle"]'); - introItems[3].element = vehicleLink; - this.nextStep(); - }) + createB4aVehicleClass().then((savedObject) => { + updateState(savedObject) + vehicleRowCreated = true; + }).then(() => { + introItems[3].element = getCustomVehicleClassLink(); + this.nextStep() + }) + }).catch(e => console.error(e)).finally(() => { + if (!unexpectedErrorThrown) { + introItems.splice(3, 2); + for (let i = 3; i < introItems.length; i++) { + introItems[i].step -= 2; + } + unexpectedErrorThrown = true; + } + console.error(e); + }); } - const nextButton = getNextButton(); nextButton.innerHTML = 'Next'; + nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); const numberLayer = document.querySelector('.introjs-helperNumberLayer'); numberLayer.style.marginLeft = 0; @@ -548,6 +582,8 @@ class Browser extends DashboardView { break; case 4: this._introItems[4].element = document.querySelector('#browser > div'); + document.querySelector('#browser').scrollLeft = 0; + targetElement.style.backgroundColor = 'inherit'; break; case 5: if(unexpectedErrorThrown) { @@ -565,9 +601,20 @@ class Browser extends DashboardView { targetElement.style.backgroundColor = 'inherit'; } break; + case 7: + this.onExit() + break; } }, onAfterChange: function(targetElement) { + const allSteps = this._introItems.map(item => item.element).filter(Boolean); + allSteps.forEach(el => { + el.style.backgroundColor = ''; + }); + + if (targetElement) { + targetElement.style.backgroundColor = '#0e69a0'; + } switch(this._currentStep) { case 0: if (this._introItems.length === 1) { @@ -576,9 +623,31 @@ class Browser extends DashboardView { targetElement.style.backgroundColor = 'inherit'; } break; + case 3: + if (!unexpectedErrorThrown) { + if (!document.querySelector('#browser > div > div:nth-child(2)')){ + // next row has not rendered yet + const nextButton = getNextButton(); + nextButton.innerHTML = `
`; + nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); + getNextComponentReadyPromise(() => document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')) + .then(() => { + nextButton.innerHTML = 'Next'; + nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); + }) + } + } + break; + case 4: + const browserEl = document.querySelector('#browser') + browserEl.style.scrollLeft = 0; + browserEl.style.pointerEvents = 'none' + break; } }, onBeforeExit: function() { + document.querySelector('#browser').style.pointerEvents = 'auto' + document.removeEventListener('keydown', blockKeys, true); // If is exiting before the last step, avoid exit and shows the last step if (this._currentStep < this._introItems.length - 1) { this._forcedStep = true; diff --git a/src/stylesheets/introjs.css b/src/stylesheets/introjs.css index 4bad379e41..87c5716f26 100644 --- a/src/stylesheets/introjs.css +++ b/src/stylesheets/introjs.css @@ -382,6 +382,23 @@ tr.introjs-showElement > th { color: darkgrey; } +.tourThirdStepStyle { + top: -300px !important; +} + +.tourThirdStepStyle > div.introjs-arrow.left-bottom { + bottom: 185px !important; +} + +.disabled-during-tour { + pointer-events: none; + user-select: none; +} +.disabled-during-tour * { + pointer-events: none; + user-select: none; +} + /* For retina screens (MacBooks) */ @media only screen and (max-width: 1728px) and (-webkit-min-device-pixel-ratio: 2), only screen and (max-width: 1728px) and (min-resolution: 192dpi) { From eaacbedcd965c5a93a947e4a24d754827533301b Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Mon, 6 Oct 2025 13:41:55 -0300 Subject: [PATCH 06/11] fix background in onBeforeExit --- src/dashboard/Data/Browser/Browser.react.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 48409bf86f..e26159fbe5 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -648,6 +648,10 @@ class Browser extends DashboardView { onBeforeExit: function() { document.querySelector('#browser').style.pointerEvents = 'auto' document.removeEventListener('keydown', blockKeys, true); + const allSteps = this._introItems.map(item => item.element).filter(Boolean); + allSteps.forEach(el => { + el.style.backgroundColor = ''; + }); // If is exiting before the last step, avoid exit and shows the last step if (this._currentStep < this._introItems.length - 1) { this._forcedStep = true; From bdffe057b2892238e27c5134da98dd91c0953ae5 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Mon, 6 Oct 2025 16:03:54 -0300 Subject: [PATCH 07/11] fix error handling --- src/dashboard/Data/Browser/Browser.react.js | 202 +++++++++----------- 1 file changed, 94 insertions(+), 108 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index e26159fbe5..f12cfd3f51 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -452,20 +452,17 @@ class Browser extends DashboardView { vehicle.set('name', 'Corolla'); vehicle.set('price', 19499); vehicle.set('color', 'black'); - + try { const savedObject = await vehicle.save(null, { useMasterKey: true }); return savedObject; } catch (error) { - console.error('Erro ao criar B4aVehicle:', error); + console.error('Error to create B4aVehicle:', error); throw error; } } - let vehicleRowCreated = false; - const reactProps = this.props; - const reactContext = this.context; - + let vehicleRowCreated = false; return { steps, @@ -482,7 +479,7 @@ class Browser extends DashboardView { user.playDatabaseBrowserTutorial = false; AccountManager.setCurrentUser({ user }); }, - onBeforeChange: function(targetElement) { + onBeforeChange: async function(targetElement) { const introItems = this._introItems; const prevEl = this._introItems[this._currentStep - 1]?.element; if (prevEl) { @@ -495,115 +492,103 @@ class Browser extends DashboardView { } else { this._forcedStep = false; } - switch(this._currentStep) { - case 0: - case 1: - { - const nextButton = getNextButton(); - if (nextButton) { - nextButton.innerHTML = 'Next'; - } - if (this._currentStep === 1) { - document.querySelector('#section_contents > div > div').style.backgroundColor = '#0e69a0'; + try { + switch(this._currentStep) { + case 0: + case 1: + { + const nextButton = getNextButton(); + if (nextButton) { + nextButton.innerHTML = 'Next'; + } + if (this._currentStep === 1) { + document.querySelector('#section_contents > div > div').style.backgroundColor = '#0e69a0'; + } } - } - break; - case 2: - { - const stepElement = document.querySelector('.introjs-helperNumberLayer'); - stepElement.style.marginLeft = '20px'; - const nextButton = getNextButton(); - nextButton.innerHTML = 'Run'; - } - break; - case 3: - { - const nextButton = getNextButton(); - if (nextButton) { - nextButton.innerHTML = `
`; - nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); + break; + case 2: + { + const stepElement = document.querySelector('.introjs-helperNumberLayer'); + stepElement.style.marginLeft = '20px'; + const nextButton = getNextButton(); + nextButton.innerHTML = 'Run'; } - - if (getCustomVehicleClassLink() && !vehicleRowCreated) { - createB4aVehicleClass().then((savedObject) => { - updateState(savedObject) - vehicleRowCreated = true; - }).then(() => { - introItems[3].element = getCustomVehicleClassLink(); - nextButton.innerHTML = 'Next'; - nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); - }).catch(e => { - if (!unexpectedErrorThrown) { - introItems.splice(3, 2); - for (let i = 3; i < introItems.length; i++) { - introItems[i].step -= 2; + break; + case 3: + { + const nextButton = getNextButton(); + + if (nextButton) { + nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); + nextButton.innerHTML = `
`; + } + + const createClassVehicle = async () => { + try { + let savedObject; + if (getCustomVehicleClassLink() && !vehicleRowCreated) { + savedObject = await createB4aVehicleClass(); + } else if (!getCustomVehicleClassLink()) { + await schema.dispatch(ActionTypes.CREATE_CLASS, { + className: 'B4aVehicle', + fields: { name: { type: 'String' }, price: { type: 'Number' }, color: { type: 'String' } } + }); + savedObject = await createB4aVehicleClass(); } - unexpectedErrorThrown = true; - } - console.error(e); - }); - } else if(!getCustomVehicleClassLink()){ - schema.dispatch(ActionTypes.CREATE_CLASS, { - className: 'B4aVehicle', - fields: { - name: { type: 'String' }, - price: { type: 'Number' }, - color: { type: 'String' }, - } - }).then(async () => { - createB4aVehicleClass().then((savedObject) => { - updateState(savedObject) - vehicleRowCreated = true; - }).then(() => { - introItems[3].element = getCustomVehicleClassLink(); - this.nextStep() - }) - }).catch(e => console.error(e)).finally(() => { - if (!unexpectedErrorThrown) { - introItems.splice(3, 2); - for (let i = 3; i < introItems.length; i++) { - introItems[i].step -= 2; + + if (savedObject) { + updateState(savedObject); + vehicleRowCreated = true; + this._introItems[3].element = getCustomVehicleClassLink(); + } + + } catch (err) { + console.error('Error on step 3:', err); + if (!unexpectedErrorThrown) { + unexpectedErrorThrown = true; + this.goToStep(7); + } + } finally { + if (nextButton) { + nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); + nextButton.innerHTML = 'Next'; } - unexpectedErrorThrown = true; } - console.error(e); - }); - } - nextButton.innerHTML = 'Next'; - nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); - - const numberLayer = document.querySelector('.introjs-helperNumberLayer'); - numberLayer.style.marginLeft = 0; - if (!unexpectedErrorThrown) { - reactProps.navigate(generatePath(reactContext, 'browser/B4aVehicle')); - // history.push(this.context.generatePath('browser/B4aVehicle')); + }; + + createClassVehicle(); + } + break; + case 4: + this._introItems[4].element = document.querySelector('#browser > div'); + document.querySelector('#browser').scrollLeft = 0; + targetElement.style.backgroundColor = 'inherit'; + break; + case 5: + if(unexpectedErrorThrown) { + targetElement.style.backgroundColor = 'inherit'; } - } - break; - case 4: - this._introItems[4].element = document.querySelector('#browser > div'); - document.querySelector('#browser').scrollLeft = 0; - targetElement.style.backgroundColor = 'inherit'; - break; - case 5: - if(unexpectedErrorThrown) { + break; + case 6: { + const nextBtn = getNextButton(); + const prevBtn = getPrevButton(); + // hide prev & next buttons + nextBtn.style.display = 'none'; + prevBtn.style.display = 'none'; + // move Done button to right + prevBtn.parentElement.style.justifyContent = 'end'; targetElement.style.backgroundColor = 'inherit'; } - break; - case 6: { - const nextBtn = getNextButton(); - const prevBtn = getPrevButton(); - // hide prev & next buttons - nextBtn.style.display = 'none'; - prevBtn.style.display = 'none'; - // move Done button to right - prevBtn.parentElement.style.justifyContent = 'end'; - targetElement.style.backgroundColor = 'inherit'; + break; + case 7: + this.onExit() + break; + } + } catch (error) { + console.error('Error on step:', e); + if (!unexpectedErrorThrown && this._introItems && this._introItems.length >= 8) { + this.goToStep(7); } - break; - case 7: - this.onExit() - break; } }, onAfterChange: function(targetElement) { @@ -625,7 +610,7 @@ class Browser extends DashboardView { break; case 3: if (!unexpectedErrorThrown) { - if (!document.querySelector('#browser > div > div:nth-child(2)')){ + if (!document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')){ // next row has not rendered yet const nextButton = getNextButton(); nextButton.innerHTML = `
`; @@ -636,6 +621,7 @@ class Browser extends DashboardView { nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); }) } + targetElement.style.backgroundColor = '#0e69a0'; } break; case 4: From ba431fbb28f22439dc72ee933a7ce7e34c5c8fdb Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Mon, 6 Oct 2025 16:50:48 -0300 Subject: [PATCH 08/11] adjusts catch in onBeforeChange --- src/dashboard/Data/Browser/Browser.react.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index f12cfd3f51..55093c4a31 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -585,10 +585,8 @@ class Browser extends DashboardView { break; } } catch (error) { - console.error('Error on step:', e); - if (!unexpectedErrorThrown && this._introItems && this._introItems.length >= 8) { - this.goToStep(7); - } + console.error('Error onBeforeChange:', error); + this.goToStep(7); } }, onAfterChange: function(targetElement) { From b94011777c66ae2abf9805f077d4986851d11830 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Mon, 6 Oct 2025 17:20:04 -0300 Subject: [PATCH 09/11] fix onAfterChange for Tour component --- src/dashboard/Data/Browser/Browser.react.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 55093c4a31..40e88da6bf 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -608,12 +608,12 @@ class Browser extends DashboardView { break; case 3: if (!unexpectedErrorThrown) { - if (!document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')){ + if (!document.querySelector('#browser div > div:nth-child(1)')){ // next row has not rendered yet const nextButton = getNextButton(); nextButton.innerHTML = `
`; nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); - getNextComponentReadyPromise(() => document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span')) + getNextComponentReadyPromise(() => document.querySelector('#browser > div > div:nth-child(1)')) .then(() => { nextButton.innerHTML = 'Next'; nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); From 2ed5d16c36e5fbd668d7bead2ab50e8675ad22f2 Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Tue, 7 Oct 2025 14:50:10 -0300 Subject: [PATCH 10/11] fix step return --- src/dashboard/Data/Browser/Browser.react.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index 40e88da6bf..bb7b44694d 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -454,7 +454,7 @@ class Browser extends DashboardView { vehicle.set('color', 'black'); try { - const savedObject = await vehicle.save(null, { useMasterKey: true }); + const savedObject = await vehicle.save(); return savedObject; } catch (error) { console.error('Error to create B4aVehicle:', error); @@ -480,6 +480,7 @@ class Browser extends DashboardView { AccountManager.setCurrentUser({ user }); }, onBeforeChange: async function(targetElement) { + document.addEventListener('keydown', blockKeys, true); const introItems = this._introItems; const prevEl = this._introItems[this._currentStep - 1]?.element; if (prevEl) { @@ -524,6 +525,7 @@ class Browser extends DashboardView { } const createClassVehicle = async () => { + let addDisabledClass = false; try { let savedObject; if (getCustomVehicleClassLink() && !vehicleRowCreated) { @@ -543,13 +545,15 @@ class Browser extends DashboardView { } } catch (err) { - console.error('Error on step 3:', err); if (!unexpectedErrorThrown) { unexpectedErrorThrown = true; + addDisabledClass = true; + nextButton.classList.add('introjs-disabled'); + nextButton.innerHTML = 'Next' this.goToStep(7); } } finally { - if (nextButton) { + if (nextButton && !addDisabledClass) { nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); nextButton.innerHTML = 'Next'; } @@ -627,6 +631,13 @@ class Browser extends DashboardView { browserEl.style.scrollLeft = 0; browserEl.style.pointerEvents = 'none' break; + case 6: + const nextBtn = getNextButton(); + const prevBtn = getPrevButton(); + + nextBtn.style.display = 'inline-block'; + prevBtn.style.display = 'inline-block'; + break; } }, onBeforeExit: function() { From 4b2ec2651db5372de58aee166816b3b14cec565a Mon Sep 17 00:00:00 2001 From: Silas Rafael Barreto Prado Date: Tue, 7 Oct 2025 14:54:58 -0300 Subject: [PATCH 11/11] fix simulate error --- src/dashboard/Data/Browser/Browser.react.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dashboard/Data/Browser/Browser.react.js b/src/dashboard/Data/Browser/Browser.react.js index bb7b44694d..bf5c9c5a32 100644 --- a/src/dashboard/Data/Browser/Browser.react.js +++ b/src/dashboard/Data/Browser/Browser.react.js @@ -454,7 +454,7 @@ class Browser extends DashboardView { vehicle.set('color', 'black'); try { - const savedObject = await vehicle.save(); + const savedObject = await vehicle.save(null, { useMasterKey: true }); return savedObject; } catch (error) { console.error('Error to create B4aVehicle:', error); @@ -560,7 +560,7 @@ class Browser extends DashboardView { } }; - createClassVehicle(); + createClassVehicle(); } break; case 4: