Altered structure of the tours to allow tour level config with an onSkip fn option. Tour steps now moved to 'steps' entry
This commit is contained in:
parent
71d7b6f8cd
commit
263df94076
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Popover, Layout, Heading, Body, Button } from "@budibase/bbui"
|
import { Popover, Layout, Heading, Body, Button, Link } from "@budibase/bbui"
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
import { TOURS } from "./tours.js"
|
import { TOURS } from "./tours.js"
|
||||||
import { goto, layout, isActive } from "@roxi/routify"
|
import { goto, layout, isActive } from "@roxi/routify"
|
||||||
|
@ -10,17 +10,19 @@
|
||||||
let tourStep
|
let tourStep
|
||||||
let tourStepIdx
|
let tourStepIdx
|
||||||
let lastStep
|
let lastStep
|
||||||
|
let skipping = false
|
||||||
|
|
||||||
$: tourNodes = { ...$store.tourNodes }
|
$: tourNodes = { ...$store.tourNodes }
|
||||||
$: tourKey = $store.tourKey
|
$: tourKey = $store.tourKey
|
||||||
$: tourStepKey = $store.tourStepKey
|
$: tourStepKey = $store.tourStepKey
|
||||||
|
$: tourOnSkip = TOURS[tourKey]?.onSkip
|
||||||
|
|
||||||
const updateTourStep = (targetStepKey, tourKey) => {
|
const updateTourStep = (targetStepKey, tourKey) => {
|
||||||
if (!tourKey) {
|
if (!tourKey) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!tourSteps?.length) {
|
if (!tourSteps?.length) {
|
||||||
tourSteps = [...TOURS[tourKey]]
|
tourSteps = [...TOURS[tourKey].steps]
|
||||||
}
|
}
|
||||||
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
|
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
|
||||||
lastStep = tourStepIdx + 1 == tourSteps.length
|
lastStep = tourStepIdx + 1 == tourSteps.length
|
||||||
|
@ -132,16 +134,28 @@
|
||||||
</Body>
|
</Body>
|
||||||
<div class="tour-footer">
|
<div class="tour-footer">
|
||||||
<div class="tour-navigation">
|
<div class="tour-navigation">
|
||||||
{#if tourStepIdx > 0}
|
{#if typeof tourOnSkip === "function"}
|
||||||
<Button
|
<!-- <Button
|
||||||
secondary
|
secondary
|
||||||
on:click={previousStep}
|
quiet
|
||||||
disabled={tourStepIdx == 0}
|
on:click={() => {
|
||||||
|
skipping = true
|
||||||
|
tourOnSkip()
|
||||||
|
}}
|
||||||
|
disabled={skipping}
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</Button> -->
|
||||||
|
<Link
|
||||||
|
quiet
|
||||||
|
on:click={() => {
|
||||||
|
skipping = true
|
||||||
|
tourOnSkip()
|
||||||
|
}}
|
||||||
|
disabled={skipping}>Skip</Link
|
||||||
>
|
>
|
||||||
<div>Back</div>
|
|
||||||
</Button>
|
|
||||||
{/if}
|
{/if}
|
||||||
<Button cta on:click={nextStep}>
|
<Button cta on:click={nextStep} disabled={skipping}>
|
||||||
<div>{lastStep ? "Finish" : "Next"}</div>
|
<div>{lastStep ? "Finish" : "Next"}</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -157,9 +171,13 @@
|
||||||
padding: var(--spacing-xl);
|
padding: var(--spacing-xl);
|
||||||
}
|
}
|
||||||
.tour-navigation {
|
.tour-navigation {
|
||||||
grid-gap: var(--spectrum-alias-grid-baseline);
|
grid-gap: var(--spacing-xl);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.tour-navigation :global(.spectrum-Link) {
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
.tour-body :global(.feature-list) {
|
.tour-body :global(.feature-list) {
|
||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
const registerTourNode = (tourKey, stepKey) => {
|
const registerTourNode = (tourKey, stepKey) => {
|
||||||
if (ready && !registered && tourKey) {
|
if (ready && !registered && tourKey) {
|
||||||
currentTourStep = TOURS[tourKey].find(step => step.id === stepKey)
|
currentTourStep = TOURS[tourKey].steps.find(step => step.id === stepKey)
|
||||||
if (!currentTourStep) {
|
if (!currentTourStep) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,34 @@ export const TOUR_KEYS = {
|
||||||
FEATURE_ONBOARDING: "feature-onboarding",
|
FEATURE_ONBOARDING: "feature-onboarding",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TOUR_BUILDER_ONBOARDING - termination
|
||||||
|
const endUserOnboarding = async () => {
|
||||||
|
// Mark the users onboarding as complete
|
||||||
|
// Clear all tour related state
|
||||||
|
if (get(auth).user) {
|
||||||
|
try {
|
||||||
|
await API.updateSelf({
|
||||||
|
onboardedAt: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update the cached user
|
||||||
|
await auth.getSelf()
|
||||||
|
|
||||||
|
store.update(state => ({
|
||||||
|
...state,
|
||||||
|
tourNodes: undefined,
|
||||||
|
tourKey: undefined,
|
||||||
|
tourKeyStep: undefined,
|
||||||
|
onboarding: false,
|
||||||
|
}))
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Onboarding failed", e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const tourEvent = eventKey => {
|
const tourEvent = eventKey => {
|
||||||
analytics.captureEvent(`${ONBOARDING_EVENT_PREFIX}:${eventKey}`, {
|
analytics.captureEvent(`${ONBOARDING_EVENT_PREFIX}:${eventKey}`, {
|
||||||
eventSource: EventSource.PORTAL,
|
eventSource: EventSource.PORTAL,
|
||||||
|
@ -28,111 +56,79 @@ const tourEvent = eventKey => {
|
||||||
|
|
||||||
const getTours = () => {
|
const getTours = () => {
|
||||||
return {
|
return {
|
||||||
[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]: [
|
[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]: {
|
||||||
{
|
onSkip: endUserOnboarding,
|
||||||
id: TOUR_STEP_KEYS.BUILDER_DATA_SECTION,
|
steps: [
|
||||||
title: "Data",
|
{
|
||||||
route: "/builder/app/:application/data",
|
id: TOUR_STEP_KEYS.BUILDER_DATA_SECTION,
|
||||||
layout: OnboardingData,
|
title: "Data",
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-data-tab",
|
route: "/builder/app/:application/data",
|
||||||
onLoad: async () => {
|
layout: OnboardingData,
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_DATA_SECTION)
|
query: ".topleftnav .spectrum-Tabs-item#builder-data-tab",
|
||||||
|
onLoad: async () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.BUILDER_DATA_SECTION)
|
||||||
|
},
|
||||||
|
align: "left",
|
||||||
},
|
},
|
||||||
align: "left",
|
{
|
||||||
},
|
id: TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION,
|
||||||
{
|
title: "Design",
|
||||||
id: TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION,
|
route: "/builder/app/:application/design",
|
||||||
title: "Design",
|
layout: OnboardingDesign,
|
||||||
route: "/builder/app/:application/design",
|
query: ".topleftnav .spectrum-Tabs-item#builder-design-tab",
|
||||||
layout: OnboardingDesign,
|
onLoad: () => {
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-design-tab",
|
tourEvent(TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION)
|
||||||
onLoad: () => {
|
},
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION)
|
align: "left",
|
||||||
},
|
},
|
||||||
align: "left",
|
{
|
||||||
},
|
id: TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION,
|
||||||
{
|
title: "Automations",
|
||||||
id: TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION,
|
route: "/builder/app/:application/automation",
|
||||||
title: "Automations",
|
query: ".topleftnav .spectrum-Tabs-item#builder-automation-tab",
|
||||||
route: "/builder/app/:application/automation",
|
body: "Once you have your app screens made, you can set up automations to fit in with your current workflow",
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-automation-tab",
|
onLoad: () => {
|
||||||
body: "Once you have your app screens made, you can set up automations to fit in with your current workflow",
|
tourEvent(TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION)
|
||||||
onLoad: () => {
|
},
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION)
|
align: "left",
|
||||||
},
|
},
|
||||||
align: "left",
|
{
|
||||||
},
|
id: TOUR_STEP_KEYS.BUILDER_USER_MANAGEMENT,
|
||||||
{
|
title: "Users",
|
||||||
id: TOUR_STEP_KEYS.BUILDER_USER_MANAGEMENT,
|
query: ".toprightnav #builder-app-users-button",
|
||||||
title: "Users",
|
body: "Add users to your app and control what level of access they have.",
|
||||||
query: ".toprightnav #builder-app-users-button",
|
onLoad: () => {
|
||||||
body: "Add users to your app and control what level of access they have.",
|
tourEvent(TOUR_STEP_KEYS.BUILDER_USER_MANAGEMENT)
|
||||||
onLoad: () => {
|
},
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_USER_MANAGEMENT)
|
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
{
|
id: TOUR_STEP_KEYS.BUILDER_APP_PUBLISH,
|
||||||
id: TOUR_STEP_KEYS.BUILDER_APP_PUBLISH,
|
title: "Publish",
|
||||||
title: "Publish",
|
layout: OnboardingPublish,
|
||||||
layout: OnboardingPublish,
|
route: "/builder/app/:application/design",
|
||||||
route: "/builder/app/:application/design",
|
endRoute: "/builder/app/:application/data",
|
||||||
endRoute: "/builder/app/:application/data",
|
query: ".toprightnav #builder-app-publish-button",
|
||||||
query: ".toprightnav #builder-app-publish-button",
|
onLoad: () => {
|
||||||
onLoad: () => {
|
tourEvent(TOUR_STEP_KEYS.BUILDER_APP_PUBLISH)
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_APP_PUBLISH)
|
},
|
||||||
|
onComplete: endUserOnboarding,
|
||||||
},
|
},
|
||||||
onComplete: async () => {
|
],
|
||||||
// Mark the users onboarding as complete
|
},
|
||||||
// Clear all tour related state
|
[TOUR_KEYS.FEATURE_ONBOARDING]: {
|
||||||
if (get(auth).user) {
|
steps: [
|
||||||
await API.updateSelf({
|
{
|
||||||
onboardedAt: new Date().toISOString(),
|
id: TOUR_STEP_KEYS.FEATURE_USER_MANAGEMENT,
|
||||||
})
|
title: "Users",
|
||||||
|
query: ".toprightnav #builder-app-users-button",
|
||||||
// Update the cached user
|
body: "Add users to your app and control what level of access they have.",
|
||||||
await auth.getSelf()
|
onLoad: () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.FEATURE_USER_MANAGEMENT)
|
||||||
store.update(state => ({
|
},
|
||||||
...state,
|
onComplete: endUserOnboarding,
|
||||||
tourNodes: undefined,
|
|
||||||
tourKey: undefined,
|
|
||||||
tourKeyStep: undefined,
|
|
||||||
onboarding: false,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
[TOUR_KEYS.FEATURE_ONBOARDING]: [
|
|
||||||
{
|
|
||||||
id: TOUR_STEP_KEYS.FEATURE_USER_MANAGEMENT,
|
|
||||||
title: "Users",
|
|
||||||
query: ".toprightnav #builder-app-users-button",
|
|
||||||
body: "Add users to your app and control what level of access they have.",
|
|
||||||
onLoad: () => {
|
|
||||||
tourEvent(TOUR_STEP_KEYS.FEATURE_USER_MANAGEMENT)
|
|
||||||
},
|
|
||||||
onComplete: async () => {
|
|
||||||
// Push the onboarding forward
|
|
||||||
if (get(auth).user) {
|
|
||||||
await API.updateSelf({
|
|
||||||
onboardedAt: new Date().toISOString(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Update the cached user
|
|
||||||
await auth.getSelf()
|
|
||||||
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
tourNodes: undefined,
|
|
||||||
tourKey: undefined,
|
|
||||||
tourKeyStep: undefined,
|
|
||||||
onboarding: false,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
const activeNav = $layout.children.find(c => $isActive(c.path))
|
const activeNav = $layout.children.find(c => $isActive(c.path))
|
||||||
const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]
|
const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]
|
||||||
const targetStep = activeNav
|
const targetStep = activeNav
|
||||||
? onboardingTour.find(step => step.route === activeNav?.path)
|
? onboardingTour.steps.find(step => step.route === activeNav?.path)
|
||||||
: null
|
: null
|
||||||
await store.update(state => ({
|
await store.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
|
|
Loading…
Reference in New Issue