Merge branch 'develop' into account-api-tests
This commit is contained in:
commit
4e296b8d1f
|
@ -98,7 +98,7 @@ jobs:
|
||||||
git fetch
|
git fetch
|
||||||
mkdir sync
|
mkdir sync
|
||||||
echo "Packaging chart to sync dir"
|
echo "Packaging chart to sync dir"
|
||||||
helm package charts/budibase --version 0.0.0-master --app-version v"$RELEASE_VERSION" --destination sync
|
helm package charts/budibase --version 0.0.0-master --app-version "$RELEASE_VERSION" --destination sync
|
||||||
echo "Packaging successful"
|
echo "Packaging successful"
|
||||||
git checkout gh-pages
|
git checkout gh-pages
|
||||||
echo "Indexing helm repo"
|
echo "Indexing helm repo"
|
||||||
|
|
|
@ -43,7 +43,7 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
|
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
|
||||||
|
|
||||||
release_tag=v${{ env.RELEASE_VERSION }}
|
release_tag=${{ env.RELEASE_VERSION }}
|
||||||
|
|
||||||
# Pull apps and worker images
|
# Pull apps and worker images
|
||||||
docker pull budibase/apps:$release_tag
|
docker pull budibase/apps:$release_tag
|
||||||
|
@ -108,8 +108,8 @@ jobs:
|
||||||
- name: Perform Github Release
|
- name: Perform Github Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
name: v${{ env.RELEASE_VERSION }}
|
name: ${{ env.RELEASE_VERSION }}
|
||||||
tag_name: v${{ env.RELEASE_VERSION }}
|
tag_name: ${{ env.RELEASE_VERSION }}
|
||||||
generate_release_notes: true
|
generate_release_notes: true
|
||||||
files: |
|
files: |
|
||||||
packages/cli/build/cli-win.exe
|
packages/cli/build/cli-win.exe
|
||||||
|
|
|
@ -71,7 +71,7 @@ jobs:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
tags: budibase/budibase,budibase/budibase:v${{ env.RELEASE_VERSION }}
|
tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }}
|
||||||
file: ./hosting/single/Dockerfile
|
file: ./hosting/single/Dockerfile
|
||||||
- name: Tag and release Budibase Azure App Service docker image
|
- name: Tag and release Budibase Azure App Service docker image
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v2
|
||||||
|
@ -80,5 +80,5 @@ jobs:
|
||||||
push: true
|
push: true
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
build-args: TARGETBUILD=aas
|
build-args: TARGETBUILD=aas
|
||||||
tags: budibase/budibase-aas,budibase/budibase-aas:v${{ env.RELEASE_VERSION }}
|
tags: budibase/budibase-aas,budibase/budibase-aas:${{ env.RELEASE_VERSION }}
|
||||||
file: ./hosting/single/Dockerfile
|
file: ./hosting/single/Dockerfile
|
||||||
|
|
|
@ -209,7 +209,7 @@ services:
|
||||||
# Override values in couchDB subchart
|
# Override values in couchDB subchart
|
||||||
couchdb:
|
couchdb:
|
||||||
## clusterSize is the initial size of the CouchDB cluster.
|
## clusterSize is the initial size of the CouchDB cluster.
|
||||||
clusterSize: 3
|
clusterSize: 1
|
||||||
allowAdminParty: false
|
allowAdminParty: false
|
||||||
|
|
||||||
# Secret Management
|
# Secret Management
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.8.12-alpha.5",
|
"version": "2.8.16-alpha.0",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
Icon,
|
Icon,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
DatePicker,
|
DatePicker,
|
||||||
|
Detail,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
import CreateWebhookModal from "components/automation/Shared/CreateWebhookModal.svelte"
|
||||||
import { automationStore, selectedAutomation } from "builderStore"
|
import { automationStore, selectedAutomation } from "builderStore"
|
||||||
|
@ -32,7 +33,7 @@
|
||||||
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
||||||
import {
|
import {
|
||||||
bindingsToCompletions,
|
bindingsToCompletions,
|
||||||
jsAutocomplete,
|
hbAutocomplete,
|
||||||
EditorModes,
|
EditorModes,
|
||||||
} from "components/common/CodeEditor"
|
} from "components/common/CodeEditor"
|
||||||
import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte"
|
import FilterDrawer from "components/design/settings/controls/FilterEditor/FilterDrawer.svelte"
|
||||||
|
@ -55,6 +56,7 @@
|
||||||
let drawer
|
let drawer
|
||||||
let fillWidth = true
|
let fillWidth = true
|
||||||
let inputData
|
let inputData
|
||||||
|
let codeBindingOpen = false
|
||||||
|
|
||||||
$: filters = lookForFilters(schemaProperties) || []
|
$: filters = lookForFilters(schemaProperties) || []
|
||||||
$: tempFilters = filters
|
$: tempFilters = filters
|
||||||
|
@ -70,6 +72,13 @@
|
||||||
$: queryLimit = tableId?.includes("datasource") ? "∞" : "1000"
|
$: queryLimit = tableId?.includes("datasource") ? "∞" : "1000"
|
||||||
$: isTrigger = block?.type === "TRIGGER"
|
$: isTrigger = block?.type === "TRIGGER"
|
||||||
$: isUpdateRow = stepId === ActionStepID.UPDATE_ROW
|
$: isUpdateRow = stepId === ActionStepID.UPDATE_ROW
|
||||||
|
$: codeMode =
|
||||||
|
stepId === "EXECUTE_BASH" ? EditorModes.Handlebars : EditorModes.JS
|
||||||
|
|
||||||
|
$: stepCompletions =
|
||||||
|
codeMode === EditorModes.Handlebars
|
||||||
|
? [hbAutocomplete([...bindingsToCompletions(bindings, codeMode)])]
|
||||||
|
: []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO - Remove after November 2023
|
* TODO - Remove after November 2023
|
||||||
|
@ -489,6 +498,18 @@
|
||||||
/>
|
/>
|
||||||
{:else if value.customType === "code"}
|
{:else if value.customType === "code"}
|
||||||
<CodeEditorModal>
|
<CodeEditorModal>
|
||||||
|
{#if codeMode == EditorModes.JS}
|
||||||
|
<ActionButton
|
||||||
|
on:click={() => (codeBindingOpen = !codeBindingOpen)}
|
||||||
|
quiet
|
||||||
|
icon={codeBindingOpen ? "ChevronDown" : "ChevronRight"}
|
||||||
|
>
|
||||||
|
<Detail size="S">Bindings</Detail>
|
||||||
|
</ActionButton>
|
||||||
|
{#if codeBindingOpen}
|
||||||
|
<pre>{JSON.stringify(bindings, null, 2)}</pre>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
value={inputData[key]}
|
value={inputData[key]}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
|
@ -496,19 +517,22 @@
|
||||||
onChange({ detail: e.detail }, key)
|
onChange({ detail: e.detail }, key)
|
||||||
inputData[key] = e.detail
|
inputData[key] = e.detail
|
||||||
}}
|
}}
|
||||||
completions={[
|
completions={stepCompletions}
|
||||||
jsAutocomplete([
|
mode={codeMode}
|
||||||
...bindingsToCompletions(bindings, EditorModes.JS),
|
autocompleteEnabled={codeMode != EditorModes.JS}
|
||||||
]),
|
|
||||||
]}
|
|
||||||
mode={EditorModes.JS}
|
|
||||||
height={500}
|
height={500}
|
||||||
/>
|
/>
|
||||||
<div class="messaging">
|
<div class="messaging">
|
||||||
<Icon name="FlashOn" />
|
{#if codeMode == EditorModes.Handlebars}
|
||||||
<div class="messaging-wrap">
|
<Icon name="FlashOn" />
|
||||||
<div>Add available bindings by typing <strong>$</strong></div>
|
<div class="messaging-wrap">
|
||||||
</div>
|
<div>
|
||||||
|
Add available bindings by typing <strong>
|
||||||
|
}}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</CodeEditorModal>
|
</CodeEditorModal>
|
||||||
{:else if value.customType === "loopOption"}
|
{:else if value.customType === "loopOption"}
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
export let mode = EditorModes.Handlebars
|
export let mode = EditorModes.Handlebars
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let placeholder = null
|
export let placeholder = null
|
||||||
|
export let autocompleteEnabled = true
|
||||||
|
|
||||||
// Export a function to expose caret position
|
// Export a function to expose caret position
|
||||||
export const getCaretPosition = () => {
|
export const getCaretPosition = () => {
|
||||||
|
@ -150,12 +151,6 @@
|
||||||
syntaxHighlighting(oneDarkHighlightStyle, { fallback: true }),
|
syntaxHighlighting(oneDarkHighlightStyle, { fallback: true }),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
highlightSpecialChars(),
|
highlightSpecialChars(),
|
||||||
autocompletion({
|
|
||||||
override: [...completions],
|
|
||||||
closeOnBlur: true,
|
|
||||||
icons: false,
|
|
||||||
optionClass: () => "autocomplete-option",
|
|
||||||
}),
|
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
EditorView.updateListener.of(v => {
|
EditorView.updateListener.of(v => {
|
||||||
const docStr = v.state.doc?.toString()
|
const docStr = v.state.doc?.toString()
|
||||||
|
@ -178,11 +173,16 @@
|
||||||
|
|
||||||
const buildExtensions = base => {
|
const buildExtensions = base => {
|
||||||
const complete = [...base]
|
const complete = [...base]
|
||||||
if (mode.name == "javascript") {
|
|
||||||
complete.push(javascript())
|
if (autocompleteEnabled) {
|
||||||
complete.push(highlightWhitespace())
|
complete.push(
|
||||||
complete.push(lineNumbers())
|
autocompletion({
|
||||||
complete.push(foldGutter())
|
override: [...completions],
|
||||||
|
closeOnBlur: true,
|
||||||
|
icons: false,
|
||||||
|
optionClass: () => "autocomplete-option",
|
||||||
|
})
|
||||||
|
)
|
||||||
complete.push(
|
complete.push(
|
||||||
EditorView.inputHandler.of((view, from, to, insert) => {
|
EditorView.inputHandler.of((view, from, to, insert) => {
|
||||||
if (insert === "$") {
|
if (insert === "$") {
|
||||||
|
@ -212,6 +212,13 @@
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode.name == "javascript") {
|
||||||
|
complete.push(javascript())
|
||||||
|
complete.push(highlightWhitespace())
|
||||||
|
complete.push(lineNumbers())
|
||||||
|
complete.push(foldGutter())
|
||||||
|
}
|
||||||
|
|
||||||
if (placeholder) {
|
if (placeholder) {
|
||||||
complete.push(placeholderFn(placeholder))
|
complete.push(placeholderFn(placeholder))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,20 @@
|
||||||
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
|
||||||
|
$: tour = TOURS[tourKey]
|
||||||
|
$: tourOnSkip = tour?.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 = [...tour.steps]
|
||||||
}
|
}
|
||||||
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
|
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
|
||||||
lastStep = tourStepIdx + 1 == tourSteps.length
|
lastStep = tourStepIdx + 1 == tourSteps.length
|
||||||
|
@ -71,23 +74,8 @@
|
||||||
tourStep.onComplete()
|
tourStep.onComplete()
|
||||||
}
|
}
|
||||||
popover.hide()
|
popover.hide()
|
||||||
if (tourStep.endRoute) {
|
if (tour.endRoute) {
|
||||||
$goto(tourStep.endRoute)
|
$goto(tour.endRoute)
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousStep = async () => {
|
|
||||||
if (tourStepIdx > 0) {
|
|
||||||
let target = tourSteps[tourStepIdx - 1]
|
|
||||||
if (target) {
|
|
||||||
store.update(state => ({
|
|
||||||
...state,
|
|
||||||
tourStepKey: target.id,
|
|
||||||
}))
|
|
||||||
navigateStep(target)
|
|
||||||
} else {
|
|
||||||
console.log("Could not retrieve step")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,16 +120,23 @@
|
||||||
</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
|
<Link
|
||||||
secondary
|
secondary
|
||||||
on:click={previousStep}
|
quiet
|
||||||
disabled={tourStepIdx == 0}
|
on:click={() => {
|
||||||
|
skipping = true
|
||||||
|
tourOnSkip()
|
||||||
|
if (tour.endRoute) {
|
||||||
|
$goto(tour.endRoute)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={skipping}
|
||||||
>
|
>
|
||||||
<div>Back</div>
|
Skip
|
||||||
</Button>
|
</Link>
|
||||||
{/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 +152,10 @@
|
||||||
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-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
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { auth } from "stores/portal"
|
||||||
import analytics from "analytics"
|
import analytics from "analytics"
|
||||||
import { OnboardingData, OnboardingDesign, OnboardingPublish } from "./steps"
|
import { OnboardingData, OnboardingDesign, OnboardingPublish } from "./steps"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
|
||||||
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
const ONBOARDING_EVENT_PREFIX = "onboarding"
|
||||||
|
|
||||||
export const TOUR_STEP_KEYS = {
|
export const TOUR_STEP_KEYS = {
|
||||||
|
@ -20,6 +21,37 @@ export const TOUR_KEYS = {
|
||||||
FEATURE_ONBOARDING: "feature-onboarding",
|
FEATURE_ONBOARDING: "feature-onboarding",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const endUserOnboarding = async ({ skipped = false } = {}) => {
|
||||||
|
// Mark the users onboarding as complete
|
||||||
|
// Clear all tour related state
|
||||||
|
if (get(auth).user) {
|
||||||
|
try {
|
||||||
|
await API.updateSelf({
|
||||||
|
onboardedAt: new Date().toISOString(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (skipped) {
|
||||||
|
tourEvent("skipped")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 +60,81 @@ const tourEvent = eventKey => {
|
||||||
|
|
||||||
const getTours = () => {
|
const getTours = () => {
|
||||||
return {
|
return {
|
||||||
[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]: [
|
[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]: {
|
||||||
{
|
steps: [
|
||||||
id: TOUR_STEP_KEYS.BUILDER_DATA_SECTION,
|
{
|
||||||
title: "Data",
|
id: TOUR_STEP_KEYS.BUILDER_DATA_SECTION,
|
||||||
route: "/builder/app/:application/data",
|
title: "Data",
|
||||||
layout: OnboardingData,
|
route: "/builder/app/:application/data",
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-data-tab",
|
layout: OnboardingData,
|
||||||
onLoad: async () => {
|
query: ".topleftnav .spectrum-Tabs-item#builder-data-tab",
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_DATA_SECTION)
|
onLoad: async () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.BUILDER_DATA_SECTION)
|
||||||
|
},
|
||||||
|
align: "left",
|
||||||
},
|
},
|
||||||
align: "left",
|
{
|
||||||
|
id: TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION,
|
||||||
|
title: "Design",
|
||||||
|
route: "/builder/app/:application/design",
|
||||||
|
layout: OnboardingDesign,
|
||||||
|
query: ".topleftnav .spectrum-Tabs-item#builder-design-tab",
|
||||||
|
onLoad: () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION)
|
||||||
|
},
|
||||||
|
align: "left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION,
|
||||||
|
title: "Automations",
|
||||||
|
route: "/builder/app/:application/automation",
|
||||||
|
query: ".topleftnav .spectrum-Tabs-item#builder-automation-tab",
|
||||||
|
body: "Once you have your app screens made, you can set up automations to fit in with your current workflow",
|
||||||
|
onLoad: () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION)
|
||||||
|
},
|
||||||
|
align: "left",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: TOUR_STEP_KEYS.BUILDER_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.BUILDER_USER_MANAGEMENT)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: TOUR_STEP_KEYS.BUILDER_APP_PUBLISH,
|
||||||
|
title: "Publish",
|
||||||
|
layout: OnboardingPublish,
|
||||||
|
route: "/builder/app/:application/design",
|
||||||
|
query: ".toprightnav #builder-app-publish-button",
|
||||||
|
onLoad: () => {
|
||||||
|
tourEvent(TOUR_STEP_KEYS.BUILDER_APP_PUBLISH)
|
||||||
|
},
|
||||||
|
onComplete: endUserOnboarding,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onSkip: async () => {
|
||||||
|
await endUserOnboarding({ skipped: true })
|
||||||
},
|
},
|
||||||
{
|
endRoute: "/builder/app/:application/data",
|
||||||
id: TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION,
|
},
|
||||||
title: "Design",
|
[TOUR_KEYS.FEATURE_ONBOARDING]: {
|
||||||
route: "/builder/app/:application/design",
|
steps: [
|
||||||
layout: OnboardingDesign,
|
{
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-design-tab",
|
id: TOUR_STEP_KEYS.FEATURE_USER_MANAGEMENT,
|
||||||
onLoad: () => {
|
title: "Users",
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION)
|
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: endUserOnboarding,
|
||||||
},
|
},
|
||||||
align: "left",
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION,
|
|
||||||
title: "Automations",
|
|
||||||
route: "/builder/app/:application/automation",
|
|
||||||
query: ".topleftnav .spectrum-Tabs-item#builder-automation-tab",
|
|
||||||
body: "Once you have your app screens made, you can set up automations to fit in with your current workflow",
|
|
||||||
onLoad: () => {
|
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_AUTOMATION_SECTION)
|
|
||||||
},
|
|
||||||
align: "left",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: TOUR_STEP_KEYS.BUILDER_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.BUILDER_USER_MANAGEMENT)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: TOUR_STEP_KEYS.BUILDER_APP_PUBLISH,
|
|
||||||
title: "Publish",
|
|
||||||
layout: OnboardingPublish,
|
|
||||||
route: "/builder/app/:application/design",
|
|
||||||
endRoute: "/builder/app/:application/data",
|
|
||||||
query: ".toprightnav #builder-app-publish-button",
|
|
||||||
onLoad: () => {
|
|
||||||
tourEvent(TOUR_STEP_KEYS.BUILDER_APP_PUBLISH)
|
|
||||||
},
|
|
||||||
onComplete: async () => {
|
|
||||||
// Mark the users onboarding as complete
|
|
||||||
// Clear all tour related state
|
|
||||||
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,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[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,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
import TourPopover from "components/portal/onboarding/TourPopover.svelte"
|
import TourPopover from "components/portal/onboarding/TourPopover.svelte"
|
||||||
import BuilderSidePanel from "./_components/BuilderSidePanel.svelte"
|
import BuilderSidePanel from "./_components/BuilderSidePanel.svelte"
|
||||||
import { UserAvatars } from "@budibase/frontend-core"
|
import { UserAvatars } from "@budibase/frontend-core"
|
||||||
import { TOUR_KEYS, TOURS } from "components/portal/onboarding/tours.js"
|
import { TOUR_KEYS } from "components/portal/onboarding/tours.js"
|
||||||
import PreviewOverlay from "./_components/PreviewOverlay.svelte"
|
import PreviewOverlay from "./_components/PreviewOverlay.svelte"
|
||||||
|
|
||||||
export let application
|
export let application
|
||||||
|
@ -87,17 +87,10 @@
|
||||||
// Check if onboarding is enabled.
|
// Check if onboarding is enabled.
|
||||||
if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) {
|
if (isEnabled(TENANT_FEATURE_FLAGS.ONBOARDING_TOUR)) {
|
||||||
if (!$auth.user?.onboardedAt) {
|
if (!$auth.user?.onboardedAt) {
|
||||||
// Determine the correct step
|
|
||||||
const activeNav = $layout.children.find(c => $isActive(c.path))
|
|
||||||
const onboardingTour = TOURS[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]
|
|
||||||
const targetStep = activeNav
|
|
||||||
? onboardingTour.find(step => step.route === activeNav?.path)
|
|
||||||
: null
|
|
||||||
await store.update(state => ({
|
await store.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
onboarding: true,
|
onboarding: true,
|
||||||
tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING,
|
tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING,
|
||||||
tourStepKey: targetStep?.id,
|
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
// Feature tour date
|
// Feature tour date
|
||||||
|
|
|
@ -8,27 +8,31 @@
|
||||||
|
|
||||||
let structureLookupMap = {}
|
let structureLookupMap = {}
|
||||||
|
|
||||||
const registerBlockComponent = (id, order, parentId, instance) => {
|
const registerBlockComponent = (id, parentId, order, instance) => {
|
||||||
// Ensure child map exists
|
// Ensure child map exists
|
||||||
if (!structureLookupMap[parentId]) {
|
if (!structureLookupMap[parentId]) {
|
||||||
structureLookupMap[parentId] = {}
|
structureLookupMap[parentId] = {}
|
||||||
}
|
}
|
||||||
// Add this instance in this order, overwriting any existing instance in
|
// Add this instance in this order, overwriting any existing instance in
|
||||||
// this order in case of repeaters
|
// this order in case of repeaters
|
||||||
structureLookupMap[parentId][order] = instance
|
structureLookupMap[parentId][id] = { order, instance }
|
||||||
}
|
}
|
||||||
|
|
||||||
const unregisterBlockComponent = (order, parentId) => {
|
const unregisterBlockComponent = (id, parentId) => {
|
||||||
// Ensure child map exists
|
// Ensure child map exists
|
||||||
if (!structureLookupMap[parentId]) {
|
if (!structureLookupMap[parentId]) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete structureLookupMap[parentId][order]
|
delete structureLookupMap[parentId][id]
|
||||||
}
|
}
|
||||||
|
|
||||||
const eject = () => {
|
const eject = () => {
|
||||||
// Start the new structure with the root component
|
// Start the new structure with the root component
|
||||||
let definition = structureLookupMap[$component.id][0]
|
const rootMap = structureLookupMap[$component.id] || {}
|
||||||
|
let definition = Object.values(rootMap)[0]?.instance
|
||||||
|
if (!definition) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Copy styles from block to root component
|
// Copy styles from block to root component
|
||||||
definition._styles = {
|
definition._styles = {
|
||||||
|
@ -49,10 +53,7 @@
|
||||||
const attachChildren = (rootComponent, map) => {
|
const attachChildren = (rootComponent, map) => {
|
||||||
// Transform map into children array
|
// Transform map into children array
|
||||||
let id = rootComponent._id
|
let id = rootComponent._id
|
||||||
const children = Object.entries(map[id] || {}).map(([order, instance]) => ({
|
const children = Object.values(map[id] || {})
|
||||||
order,
|
|
||||||
instance,
|
|
||||||
}))
|
|
||||||
if (!children.length) {
|
if (!children.length) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
// Create a fake component instance so that we can use the core Component
|
// Create a fake component instance so that we can use the core Component
|
||||||
// to render this part of the block, taking advantage of binding enrichment
|
// to render this part of the block, taking advantage of binding enrichment
|
||||||
$: id = `${block.id}-${context ?? rand}`
|
$: id = `${block.id}-${context ?? rand}`
|
||||||
|
$: parentId = $component?.id
|
||||||
|
$: inBuilder = $builderStore.inBuilder
|
||||||
$: instance = {
|
$: instance = {
|
||||||
_component: `@budibase/standard-components/${type}`,
|
_component: `@budibase/standard-components/${type}`,
|
||||||
_id: id,
|
_id: id,
|
||||||
|
@ -38,14 +40,14 @@
|
||||||
// Register this block component if we're inside the builder so it can be
|
// Register this block component if we're inside the builder so it can be
|
||||||
// ejected later
|
// ejected later
|
||||||
$: {
|
$: {
|
||||||
if ($builderStore.inBuilder) {
|
if (inBuilder) {
|
||||||
block.registerComponent(id, order ?? 0, $component?.id, instance)
|
block.registerComponent(id, parentId, order ?? 0, instance)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if ($builderStore.inBuilder) {
|
if (inBuilder) {
|
||||||
block.unregisterComponent(order ?? 0, $component?.id)
|
block.unregisterComponent(id, parentId)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
order={1}
|
order={1}
|
||||||
>
|
>
|
||||||
{#if enrichedSearchColumns?.length}
|
{#if enrichedSearchColumns?.length}
|
||||||
{#each enrichedSearchColumns as column, idx}
|
{#each enrichedSearchColumns as column, idx (column.name)}
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type={column.componentType}
|
type={column.componentType}
|
||||||
props={{
|
props={{
|
||||||
|
|
|
@ -170,7 +170,7 @@
|
||||||
order={1}
|
order={1}
|
||||||
>
|
>
|
||||||
{#if enrichedSearchColumns?.length}
|
{#if enrichedSearchColumns?.length}
|
||||||
{#each enrichedSearchColumns as column, idx}
|
{#each enrichedSearchColumns as column, idx (column.name)}
|
||||||
<BlockComponent
|
<BlockComponent
|
||||||
type={column.componentType}
|
type={column.componentType}
|
||||||
props={{
|
props={{
|
||||||
|
|
|
@ -478,7 +478,7 @@ export const enrichButtonActions = (actions, context) => {
|
||||||
actions.slice(i + 1),
|
actions.slice(i + 1),
|
||||||
newContext
|
newContext
|
||||||
)
|
)
|
||||||
resolve(await next())
|
resolve(typeof next === "function" ? await next() : true)
|
||||||
} else {
|
} else {
|
||||||
resolve(false)
|
resolve(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,6 @@ node ./bumpVersion.js $1
|
||||||
NEW_VERSION=$(node -p "require('../lerna.json').version")
|
NEW_VERSION=$(node -p "require('../lerna.json').version")
|
||||||
git add ../lerna.json
|
git add ../lerna.json
|
||||||
git commit -m "Bump version to $NEW_VERSION"
|
git commit -m "Bump version to $NEW_VERSION"
|
||||||
git tag v$NEW_VERSION
|
git tag $NEW_VERSION
|
||||||
git push
|
git push
|
||||||
git push --tags
|
git push --tags
|
Loading…
Reference in New Issue