const B4aVehicle = Parse.Object.extend('B4aVehicle');@@ -346,28 +379,28 @@ 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', - element: () => document.querySelectorAll('.section')[2], + element: () => document.querySelectorAll('.section')[3], intro: `It’s very simple to save data on Back4App from your front-end.
Click on the Run button to execute this code.
`, position: 'right', - tooltipClass: 'tourThirdStepStyle' + tooltipClass: 'tourThirdStepStyle', }, { eventId: 'Custom Class Link', - element: () => document.querySelector('.class_list [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' }, { @@ -399,7 +432,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 => { @@ -412,25 +445,47 @@ 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('Error to create B4aVehicle:', error); + throw error; + } + } + + let vehicleRowCreated = false; + 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; AccountManager.setCurrentUser({ user }); }, - onBeforeChange: function(targetElement) { + onBeforeChange: async function(targetElement) { + document.addEventListener('keydown', blockKeys, true); 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}`) @@ -438,91 +493,115 @@ class Browser extends DashboardView { } else { this._forcedStep = false; } - switch(this._currentStep) { - case 0: - case 1: - { - const nextButton = getNextButton(); - if (nextButton) { - nextButton.innerHTML = 'Next'; + 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'; + } } - 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 2: - { - const stepElement = document.querySelector('.introjs-helperNumberLayer'); - stepElement.style.marginLeft = '20px'; - const nextButton = getNextButton(); - nextButton.innerHTML = 'Run'; - } - break; - case 3: - { - if (!getCustomVehicleClassLink() && !unexpectedErrorThrown) { - schema.dispatch(ActionTypes.CREATE_CLASS, { - className: 'B4aVehicle', - fields: { - name: { type: 'String' }, - price: { type: 'Number' }, - color: { type: 'String' }, - } - }).then(() => { - return context.apiRequest('POST', '/classes/B4aVehicle', { name: 'Corolla', price: 19499, color: 'black' }, { useMasterKey: true }); - }).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; + break; + case 3: + { + const nextButton = getNextButton(); + + if (nextButton) { + nextButton.classList.add('introjs-disabled', styles.tourLoadingBtn); + nextButton.innerHTML = ``; + } + + const createClassVehicle = async () => { + let addDisabledClass = false; + 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(); + } + + if (savedObject) { + updateState(savedObject); + vehicleRowCreated = true; + this._introItems[3].element = getCustomVehicleClassLink(); + } + + } catch (err) { + if (!unexpectedErrorThrown) { + unexpectedErrorThrown = true; + addDisabledClass = true; + nextButton.classList.add('introjs-disabled'); + nextButton.innerHTML = 'Next' + this.goToStep(7); + } + } finally { + if (nextButton && !addDisabledClass) { + nextButton.classList.remove('introjs-disabled', styles.tourLoadingBtn); + nextButton.innerHTML = 'Next'; } - unexpectedErrorThrown = true; } - console.error(e); - this.nextStep(); - }); - return false; - } - const nextButton = getNextButton(); - nextButton.innerHTML = 'Next'; - - const numberLayer = document.querySelector('.introjs-helperNumberLayer'); - numberLayer.style.marginLeft = 0; - if (!unexpectedErrorThrown) { - this.props.navigate(generatePath(this.context, '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: - if (!unexpectedErrorThrown) { - this._introItems[4].element = document.querySelector('[class^=browser] [class^=tableRow] > :nth-child(2) span'); - } - 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; } - break; + } catch (error) { + console.error('Error onBeforeChange:', error); + this.goToStep(7); } }, 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) { @@ -533,12 +612,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); @@ -547,9 +626,27 @@ class Browser extends DashboardView { targetElement.style.backgroundColor = '#0e69a0'; } break; + case 4: + const browserEl = document.querySelector('#browser') + 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() { + 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; @@ -748,7 +845,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 +857,10 @@ 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`)) { + return; + } + this.showNote(errorDeletingNote, true); }).finally(() => { this.setState({ showCreateClassDialog: false }); diff --git a/src/stylesheets/introjs.css b/src/stylesheets/introjs.css index 223abde6f5..87c5716f26 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,31 @@ tr.introjs-showElement > th { color: darkgrey; } -@media only screen and (max-width: 1440px) { +.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) { .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