1
0
Fork 0
mirror of synced 2024-07-15 11:15:59 +12:00

Merge branch 'develop' into Cypress

This commit is contained in:
Mitch-Budibase 2023-01-31 18:19:43 +00:00
commit ad0bba5fc2
41 changed files with 3629 additions and 1637 deletions

View file

@ -1,5 +1,5 @@
{
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"npmClient": "yarn",
"packages": [
"packages/*"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@ -23,7 +23,7 @@
},
"dependencies": {
"@budibase/nano": "10.1.1",
"@budibase/types": "2.2.12-alpha.50",
"@budibase/types": "2.2.12-alpha.52",
"@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0",

View file

@ -13,6 +13,7 @@ import {
UserPermissionAssignedEvent,
UserPermissionRemovedEvent,
UserUpdatedEvent,
UserOnboardingEvent,
} from "@budibase/types"
async function created(user: User, timestamp?: number) {
@ -36,6 +37,13 @@ async function deleted(user: User) {
await publishEvent(Event.USER_DELETED, properties)
}
export async function onboardingComplete(user: User) {
const properties: UserOnboardingEvent = {
userId: user._id as string,
}
await publishEvent(Event.USER_ONBOARDING_COMPLETE, properties)
}
// PERMISSIONS
async function permissionAdminAssigned(user: User, timestamp?: number) {
@ -126,6 +134,7 @@ export default {
permissionAdminRemoved,
permissionBuilderAssigned,
permissionBuilderRemoved,
onboardingComplete,
invited,
inviteAccepted,
passwordForceReset,

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.52",
"@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1",

View file

@ -3,6 +3,9 @@ export default function positionDropdown(
{ anchor, align, maxWidth, useAnchorWidth }
) {
const update = () => {
if (!anchor) {
return
}
const anchorBounds = anchor.getBoundingClientRect()
const elementBounds = element.getBoundingClientRect()
let styles = {
@ -13,6 +16,8 @@ export default function positionDropdown(
top: null,
}
let popoverLeftPad = 20
// Determine vertical styles
if (window.innerHeight - anchorBounds.bottom < 100) {
styles.top = anchorBounds.top - elementBounds.height - 5
@ -29,7 +34,13 @@ export default function positionDropdown(
styles.minWidth = anchorBounds.width
}
if (align === "right") {
styles.left = anchorBounds.left + anchorBounds.width - elementBounds.width
let left =
anchorBounds.left + anchorBounds.width / 2 - elementBounds.width
// Accommodate margin on popover: 1.25rem; ~20px
if (left + elementBounds.width + popoverLeftPad > window.innerWidth) {
left -= 20
}
styles.left = left
} else if (align === "right-side") {
styles.left = anchorBounds.left + anchorBounds.width
} else {
@ -54,8 +65,11 @@ export default function positionDropdown(
const resizeObserver = new ResizeObserver(entries => {
entries.forEach(update)
})
resizeObserver.observe(anchor)
if (anchor) {
resizeObserver.observe(anchor)
}
resizeObserver.observe(element)
resizeObserver.observe(document.body)
document.addEventListener("scroll", update, true)

View file

@ -15,11 +15,13 @@
export let tooltip = undefined
export let dataCy
export let newStyles = true
export let id
let showTooltip = false
</script>
<button
{id}
class:spectrum-Button--cta={cta}
class:spectrum-Button--primary={primary}
class:spectrum-Button--secondary={secondary}

View file

@ -19,9 +19,7 @@
export let showTip = false
export let open = false
export let useAnchorWidth = false
let tipSvg =
'<svg xmlns="http://www.w3.org/svg/2000" width="23" height="12" class="spectrum-Popover-tip" > <path class="spectrum-Popover-tip-triangle" d="M 0.7071067811865476 0 L 11.414213562373096 10.707106781186548 L 22.121320343559645 0" /> </svg>'
export let dismissible = true
$: tooltipClasses = showTip
? `spectrum-Popover--withTip spectrum-Popover--${direction}`
@ -67,9 +65,15 @@
<Portal {target}>
<div
tabindex="0"
use:positionDropdown={{ anchor, align, maxWidth, useAnchorWidth }}
use:positionDropdown={{
anchor,
align,
maxWidth,
useAnchorWidth,
showTip: false,
}}
use:clickOutside={{
callback: handleOutsideClick,
callback: dismissible ? handleOutsideClick : () => {},
anchor,
}}
on:keydown={handleEscape}
@ -78,10 +82,6 @@
data-cy={dataCy}
transition:fly|local={{ y: -20, duration: 200 }}
>
{#if showTip}
{@html tipSvg}
{/if}
<slot />
</div>
</Portal>

View file

@ -3,6 +3,7 @@
import Portal from "svelte-portal"
export let title
export let icon = ""
export let id
const dispatch = createEventDispatcher()
let selected = getContext("tab")
@ -31,10 +32,7 @@
$: {
if ($selected.title === title && tab_internal) {
if ($selected.info?.left !== tab_internal.getBoundingClientRect().left) {
$selected = {
...$selected,
info: tab_internal.getBoundingClientRect(),
}
setTabInfo()
}
}
}
@ -50,6 +48,7 @@
</script>
<div
{id}
bind:this={tab_internal}
on:click={onClick}
class:is-selected={$selected.title === title}

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"license": "GPL-3.0",
"private": true,
"scripts": {
@ -58,10 +58,13 @@
}
},
"dependencies": {
"@budibase/bbui": "2.2.12-alpha.50",
"@budibase/client": "2.2.12-alpha.50",
"@budibase/frontend-core": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/bbui": "2.2.12-alpha.52",
"@budibase/client": "2.2.12-alpha.52",
"@budibase/frontend-core": "2.2.12-alpha.52",
"@budibase/string-templates": "2.2.12-alpha.52",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",
"@sentry/browser": "5.19.1",
"@spectrum-css/accordion": "^3.0.24",
"@spectrum-css/page": "^3.0.1",

View file

@ -3,7 +3,6 @@
import { routes } from "../.routify/routes"
import { NotificationDisplay, BannerDisplay } from "@budibase/bbui"
import { parse, stringify } from "qs"
import HelpIcon from "components/common/HelpIcon.svelte"
import LicensingOverlays from "components/portal/licensing/LicensingOverlays.svelte"
const queryHandler = { parse, stringify }
@ -15,7 +14,6 @@
<LicensingOverlays />
<Router {routes} config={{ queryHandler }} />
<div class="modal-container" />
<HelpIcon />
<style>
.modal-container {

View file

@ -63,6 +63,10 @@ const INITIAL_FRONTEND_STATE = {
selectedScreenId: null,
selectedComponentId: null,
selectedLayoutId: null,
// onboarding
onboarding: false,
tourNodes: null,
}
export const getFrontendStore = () => {

View file

@ -0,0 +1,31 @@
<script context="module">
import { dom, library } from "@fortawesome/fontawesome-svg-core"
import {
faEnvelope,
faXmark,
faBook,
faPlay,
faLock,
} from "@fortawesome/free-solid-svg-icons"
import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"
library.add(faXmark, faBook, faPlay, faLock, faGithub, faDiscord, faEnvelope)
dom.watch()
</script>
<script>
export let name
</script>
<span>
<i class={name} />
</span>
<style>
span {
display: contents;
}
.svg-inline--fa {
color: var(--spectrum-body-m-text-color, var(--spectrum-alias-text-color));
}
</style>

View file

@ -1,42 +0,0 @@
<script>
import { Icon, Body } from "@budibase/bbui"
</script>
<a target="_blank" href="https://github.com/Budibase/budibase/discussions">
<div class="inner hoverable">
<div class="hidden hoverable">
<Body size="S">Need help? Go to our forums</Body>
</div>
<Icon name="Help" size="XXL" />
</div>
</a>
<style>
.inner {
display: flex;
align-items: center;
gap: var(--spacing-s);
}
.inner :global(*) {
pointer-events: all;
transition: color var(--spectrum-global-animation-duration-100, 130ms);
}
.inner:hover :global(*) {
color: var(--spectrum-alias-icon-color-selected-hover);
cursor: pointer;
}
a {
color: inherit;
position: absolute;
bottom: var(--spacing-m);
right: var(--spacing-m);
border-radius: 55%;
z-index: 99999;
}
.hidden {
display: none;
}
.inner:hover .hidden {
display: block;
}
</style>

View file

@ -0,0 +1,182 @@
<script>
import FontAwesomeIcon from "./FontAwesomeIcon.svelte"
import { Popover, Heading, Body } from "@budibase/bbui"
import { licensing } from "stores/portal"
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
$: isPremiumUser = $licensing.license && !$licensing.isFreePlan
let show
let hide
let popoverAnchor
</script>
<div bind:this={popoverAnchor} class="help">
<button class="openMenu" on:click={show}>Help</button>
<Popover
class="helpMenuPopoverOverride"
bind:show
bind:hide
anchor={popoverAnchor}
>
<nav class="helpMenu">
<div class="header">
<Heading size="XS">Help resources</Heading>
<button on:click={hide} class="closeButton">
<FontAwesomeIcon name="fa-solid fa-xmark" />
</button>
</div>
<div class="divider" />
<a target="_blank" href="https://docs.budibase.com/docs">
<div class="icon">
<FontAwesomeIcon name="fa-solid fa-book" />
</div>
<Body size="S">Help docs</Body>
</a>
<div class="divider" />
<a
target="_blank"
href="https://github.com/Budibase/budibase/discussions"
>
<div class="icon">
<FontAwesomeIcon name="fa-brands fa-github" />
</div>
<Body size="S">Discussions</Body>
</a>
<div class="divider" />
<a target="_blank" href="https://discord.com/invite/ZepTmGbtfF">
<div class="icon">
<FontAwesomeIcon name="fa-brands fa-discord" />
</div>
<Body size="S">Discord</Body>
</a>
<div class="divider" />
<a target="_blank" href="https://vimeo.com/showcase/budibase-university">
<div class="icon">
<FontAwesomeIcon name="fa-solid fa-play" />
</div>
<Body size="S">Budibase University</Body>
</a>
<div class="divider" />
{#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING)}
<a
href={isPremiumUser
? "mailto:support@budibase.com"
: "/builder/portal/account/usage"}
>
<div class="premiumLinkContent" class:disabled={!isPremiumUser}>
<div class="icon">
<FontAwesomeIcon name="fa-solid fa-envelope" />
</div>
<Body size="S">Email support</Body>
</div>
{#if !isPremiumUser}
<div class="premiumBadge">
<div class="icon">
<FontAwesomeIcon name="fa-solid fa-lock" />
</div>
<Body size="XS">Premium</Body>
</div>
{/if}
</a>
{/if}
</nav>
</Popover>
</div>
<style>
.help {
z-index: 2;
position: absolute;
bottom: var(--spacing-xl);
right: 24px;
}
.openMenu {
cursor: pointer;
background-color: #6a1dc8;
border-radius: 100px;
color: white;
border: none;
font-size: 13px;
font-weight: 600;
padding: 10px 18px;
}
.helpMenu {
background-color: var(--background-alt);
overflow: hidden;
border-radius: 4px;
}
nav {
min-width: 280px;
}
.divider {
border-bottom: 1px solid var(--spectrum-global-color-gray-300);
}
.header {
display: flex;
align-items: center;
padding: 0 0 0 16px;
}
.closeButton {
cursor: pointer;
font-size: 13px;
color: var(--grey-6);
background-color: transparent;
border: none;
padding: 18px 16px;
margin-left: auto;
}
.closeButton:hover {
color: var(--grey-8);
}
a {
text-decoration: none;
color: white;
display: flex;
padding: 12px;
align-items: center;
transition: filter 0.5s;
}
a:hover {
background-color: var(--spectrum-global-color-gray-300);
}
a:last-child {
padding: 8px 12px;
}
.icon {
font-size: 13px;
margin-right: 7px;
min-width: 18px;
justify-content: center;
display: flex;
}
.premiumLinkContent {
display: flex;
align-items: center;
}
.disabled {
opacity: 70%;
}
.premiumBadge {
align-items: center;
margin-left: auto;
display: flex;
border: var(--border-light);
border-radius: 4px;
padding: 4px 7px 5px 8px;
}
</style>

View file

@ -11,6 +11,8 @@
import { store } from "builderStore"
import { ProgressCircle } from "@budibase/bbui"
import CopyInput from "components/common/inputs/CopyInput.svelte"
import TourWrap from "../portal/onboarding/TourWrap.svelte"
import { TOUR_STEP_KEYS } from "../portal/onboarding/tours.js"
let publishModal
let asyncModal
@ -54,7 +56,11 @@
}
</script>
<Button cta on:click={publishModal.show}>Publish</Button>
<TourWrap tourStepKey={TOUR_STEP_KEYS.BUILDER_APP_PUBLISH}>
<Button cta on:click={publishModal.show} id={"builder-app-publish-button"}>
Publish
</Button>
</TourWrap>
<Modal bind:this={publishModal}>
<ModalContent
title="Publish to production"

View file

@ -0,0 +1,173 @@
<script>
import { Popover, Layout, Heading, Body, Button } from "@budibase/bbui"
import { store } from "builderStore"
import { TOURS } from "./tours.js"
import { goto, layout, isActive } from "@roxi/routify"
let popoverAnchor
let popover
let tourSteps = null
let tourStep
let tourStepIdx
let lastStep
$: tourNodes = { ...$store.tourNodes }
$: tourKey = $store.tourKey
$: tourStepKey = $store.tourStepKey
const initTour = targetKey => {
if (!targetKey) {
return
}
tourSteps = [...TOURS[targetKey]]
tourStepIdx = 0
tourStep = { ...tourSteps[tourStepIdx] }
}
$: initTour(tourKey)
const updateTourStep = targetStepKey => {
if (!tourSteps?.length) {
return
}
tourStepIdx = getCurrentStepIdx(tourSteps, targetStepKey)
lastStep = tourStepIdx + 1 == tourSteps.length
tourStep = { ...tourSteps[tourStepIdx] }
tourStep.onLoad()
}
$: updateTourStep(tourStepKey)
const showPopover = (tourStep, tourNodes, popover) => {
if (!tourStep) {
return
}
popoverAnchor = tourNodes[tourStep.id]
popover?.show()
}
$: showPopover(tourStep, tourNodes, popover)
const navigateStep = step => {
if (step.route) {
const activeNav = $layout.children.find(c => $isActive(c.path))
if (activeNav) {
store.update(state => {
if (!state.previousTopNavPath) state.previousTopNavPath = {}
state.previousTopNavPath[activeNav.path] = window.location.pathname
$goto(state.previousTopNavPath[step.route] || step.route)
return state
})
}
}
}
const nextStep = async () => {
if (!lastStep === true) {
let target = tourSteps[tourStepIdx + 1]
if (target) {
store.update(state => ({
...state,
tourStepKey: target.id,
}))
navigateStep(target)
} else {
console.log("Could not retrieve step")
}
} else {
if (typeof tourStep.onComplete === "function") {
tourStep.onComplete()
}
popover.hide()
}
}
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")
}
}
}
const getCurrentStepIdx = (steps, tourStepKey) => {
if (!steps?.length) {
return
}
if (steps?.length && !tourStepKey) {
return 0
}
return steps.findIndex(step => step.id === tourStepKey)
}
</script>
{#key tourStepKey}
<Popover
align={tourStep?.align}
bind:this={popover}
anchor={popoverAnchor}
dataCy="tour-popover-menu"
maxWidth={300}
dismissible={false}
>
<Layout gap="M">
<div class="tour-header">
<Heading size="XS">{tourStep?.title || "-"}</Heading>
<div>{`${tourStepIdx + 1}/${tourSteps?.length}`}</div>
</div>
<Body size="S">
<span class="tour-body">
{#if tourStep.layout}
<svelte:component this={tourStep.layout} />
{:else}
{tourStep?.body || ""}
{/if}
</span>
</Body>
<div class="tour-footer">
<div class="tour-navigation">
{#if tourStepIdx > 0}
<Button
secondary
on:click={previousStep}
disabled={tourStepIdx == 0}
>
<div>Back</div>
</Button>
{/if}
<Button cta on:click={nextStep}>
<div>{lastStep ? "Finish" : "Next"}</div>
</Button>
</div>
</div>
</Layout>
</Popover>
{/key}
<style>
.tour-navigation {
grid-gap: var(--spectrum-alias-grid-baseline);
display: flex;
justify-content: end;
}
:global([data-cy="tour-popover-menu"]) {
padding: 10px;
margin-top: var(--spacing-l);
}
.tour-body :global(.feature-list) {
margin-bottom: 0px;
padding-left: var(--spacing-xl);
}
.tour-header {
display: flex;
align-items: center;
justify-content: space-between;
}
</style>

View file

@ -0,0 +1,29 @@
<script>
import { tourHandler } from "./tourHandler"
import { TOURS } from "./tours"
import { onMount, onDestroy } from "svelte"
import { store } from "builderStore"
export let tourStepKey
let currentTour
let ready = false
let handler
onMount(() => {
if (!$store.tourKey) return
currentTour = TOURS[$store.tourKey].find(step => step.id === tourStepKey)
const elem = document.querySelector(currentTour.query)
handler = tourHandler(elem, tourStepKey)
ready = true
})
onDestroy(() => {
if (handler) {
handler.destroy()
}
})
</script>
<slot />

View file

@ -0,0 +1,10 @@
<div>
In this section you can mange the data for your app:
<ul class="feature-list">
<li>Connect data sources</li>
<li>Edit data</li>
<li>Manage read & write access</li>
<li>Create views</li>
<li>Add bindings</li>
</ul>
</div>

View file

@ -0,0 +1,10 @@
<div>
After setting up your data, Design is where you build the screens for your
app:
<ul class="feature-list">
<li>Add screens</li>
<li>Add components</li>
<li>Choose your theme</li>
<li>Edit navigation</li>
</ul>
</div>

View file

@ -0,0 +1,7 @@
<div>
Once youre happy with your app you can publish it to production!
<p>
After publishing, any changes you make will not take affect until you next
publish.
</p>
</div>

View file

@ -0,0 +1,3 @@
export { default as OnboardingData } from "./OnboardingData.svelte"
export { default as OnboardingDesign } from "./OnboardingDesign.svelte"
export { default as OnboardingPublish } from "./OnboardingPublish.svelte"

View file

@ -0,0 +1,47 @@
import { store } from "builderStore/index"
import { get } from "svelte/store"
const registerNode = async (node, tourStepKey) => {
if (!node) {
console.log("Tour Handler - an anchor node is required")
}
if (!get(store).tourKey) {
console.log("Tour Handler - No active tour ", tourStepKey, node)
return
}
store.update(state => {
const update = {
...state,
tourNodes: {
...state.tourNodes,
[tourStepKey]: node,
},
}
return update
})
}
export function tourHandler(node, tourStepKey) {
if (node && tourStepKey) {
registerNode(node, tourStepKey)
}
return {
destroy: () => {
const updatedTourNodes = get(store).tourNodes
if (updatedTourNodes && updatedTourNodes[tourStepKey]) {
delete updatedTourNodes[tourStepKey]
store.update(state => {
const update = {
...state,
tourNodes: {
...updatedTourNodes,
},
}
return update
})
}
},
}
}

View file

@ -0,0 +1,95 @@
import { get } from "svelte/store"
import { store } from "builderStore"
import { users, auth } from "stores/portal"
import analytics from "analytics"
import { OnboardingData, OnboardingDesign, OnboardingPublish } from "./steps"
const ONBOARDING_EVENT_PREFIX = "onboarding"
export const TOUR_STEP_KEYS = {
BUILDER_APP_PUBLISH: "builder-app-publish",
BUILDER_DATA_SECTION: "builder-data-section",
BUILDER_DESIGN_SECTION: "builder-design-section",
BUILDER_AUTOMATE_SECTION: "builder-automate-section",
}
export const TOUR_KEYS = {
TOUR_BUILDER_ONBOARDING: "builder-onboarding",
}
const tourEvent = eventKey => {
analytics.captureEvent(`${ONBOARDING_EVENT_PREFIX}:${eventKey}`, {
eventSource: EventSource.PORTAL,
})
}
const getTours = () => {
return {
[TOUR_KEYS.TOUR_BUILDER_ONBOARDING]: [
{
id: TOUR_STEP_KEYS.BUILDER_DATA_SECTION,
title: "Data",
route: "/builder/app/:application/data",
layout: OnboardingData,
query: ".topcenternav .spectrum-Tabs-item#builder-data-tab",
onLoad: async () => {
tourEvent(TOUR_STEP_KEYS.BUILDER_DATA_SECTION)
},
align: "left",
},
{
id: TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION,
title: "Design",
route: "/builder/app/:application/design",
layout: OnboardingDesign,
query: ".topcenternav .spectrum-Tabs-item#builder-design-tab",
onLoad: () => {
tourEvent(TOUR_STEP_KEYS.BUILDER_DESIGN_SECTION)
},
align: "left",
},
{
id: TOUR_STEP_KEYS.BUILDER_AUTOMATE_SECTION,
title: "Automations",
route: "/builder/app/:application/automate",
query: ".topcenternav .spectrum-Tabs-item#builder-automate-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_AUTOMATE_SECTION)
},
align: "left",
},
{
id: TOUR_STEP_KEYS.BUILDER_APP_PUBLISH,
title: "Publish",
layout: OnboardingPublish,
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 users.save({
...get(auth).user,
onboardedAt: new Date().toISOString(),
})
// Update the cached user
await auth.getSelf()
store.update(state => ({
...state,
tourNodes: undefined,
tourKey: undefined,
tourKeyStep: undefined,
onboarding: false,
}))
}
},
},
],
}
}
export const TOURS = getTours()

View file

@ -1,6 +1,7 @@
<script>
import { store, automationStore } from "builderStore"
import { roles, flags } from "stores/backend"
import { auth } from "stores/portal"
import {
ActionMenu,
MenuItem,
@ -10,6 +11,7 @@
Heading,
notifications,
} from "@budibase/bbui"
import RevertModal from "components/deploy/RevertModal.svelte"
import VersionModal from "components/deploy/VersionModal.svelte"
import DeployNavigation from "components/deploy/DeployNavigation.svelte"
@ -17,6 +19,9 @@
import { isActive, goto, layout, redirect } from "@roxi/routify"
import { capitalise } from "helpers"
import { onMount, onDestroy } from "svelte"
import TourWrap from "components/portal/onboarding/TourWrap.svelte"
import TourPopover from "components/portal/onboarding/TourPopover.svelte"
import { TOUR_KEYS, TOURS } from "components/portal/onboarding/tours.js"
export let application
@ -62,6 +67,23 @@
})
}
const initTour = async () => {
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 => ({
...state,
onboarding: true,
tourKey: TOUR_KEYS.TOUR_BUILDER_ONBOARDING,
tourStepKey: targetStep?.id,
}))
}
}
onMount(async () => {
if (!hasSynced && application) {
try {
@ -69,6 +91,7 @@
// check if user has beta access
// const betaResponse = await API.checkBetaAccess($auth?.user?.email)
// betaAccess = betaResponse.access
initTour()
} catch (error) {
notifications.error("Failed to sync with production database")
}
@ -88,6 +111,7 @@
<!-- This should probably be some kind of loading state? -->
<div class="loading" />
{:then _}
<TourPopover />
<div class="root">
<div class="top-nav">
<div class="topleftnav">
@ -140,12 +164,15 @@
<div class="topcenternav">
<Tabs {selected} size="M">
{#each $layout.children as { path, title }}
<Tab
quiet
selected={$isActive(path)}
on:click={topItemNavigate(path)}
title={capitalise(title)}
/>
<TourWrap tourStepKey={`builder-${title}-section`}>
<Tab
quiet
selected={$isActive(path)}
on:click={topItemNavigate(path)}
title={capitalise(title)}
id={`builder-${title}-tab`}
/>
</TourWrap>
{/each}
</Tabs>
</div>

View file

@ -2,26 +2,29 @@
import { Button } from "@budibase/bbui"
import { goto } from "@roxi/routify"
import { auth, admin } from "stores/portal"
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
</script>
{#if $admin.cloud && $auth?.user?.accountPortalAccess}
<Button
cta
size="S"
on:click
on:click={() => {
window.open($admin.accountPortalUrl + "/portal/upgrade", "_blank")
}}
>
Upgrade
</Button>
{:else if !$admin.cloud && $auth.isAdmin}
<Button
cta
size="S"
on:click={() => $goto("/builder/portal/account/upgrade")}
on:click
>
Upgrade
</Button>
{#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING)}
{#if $admin.cloud && $auth?.user?.accountPortalAccess}
<Button
cta
size="S"
on:click
on:click={() => {
window.open($admin.accountPortalUrl + "/portal/upgrade", "_blank")
}}
>
Upgrade
</Button>
{:else if !$admin.cloud && $auth.isAdmin}
<Button
cta
size="S"
on:click={() => $goto("/builder/portal/account/upgrade")}
on:click
>
Upgrade
</Button>
{/if}
{/if}

View file

@ -7,6 +7,7 @@
import MobileMenu from "./_components/MobileMenu.svelte"
import Logo from "./_components/Logo.svelte"
import UserDropdown from "./_components/UserDropdown.svelte"
import HelpMenu from "components/common/HelpMenu.svelte"
let loaded = false
let mobileMenuVisible = false
@ -46,6 +47,7 @@
</script>
{#if $auth.user && loaded}
<HelpMenu />
<div class="container">
<div class="nav">
<div class="branding">

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {
@ -26,9 +26,9 @@
"outputPath": "build"
},
"dependencies": {
"@budibase/backend-core": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/types": "2.2.12-alpha.50",
"@budibase/backend-core": "2.2.12-alpha.52",
"@budibase/string-templates": "2.2.12-alpha.52",
"@budibase/types": "2.2.12-alpha.52",
"axios": "0.21.2",
"chalk": "4.1.0",
"cli-progress": "3.11.2",

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -19,9 +19,9 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "2.2.12-alpha.50",
"@budibase/frontend-core": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/bbui": "2.2.12-alpha.52",
"@budibase/frontend-core": "2.2.12-alpha.52",
"@budibase/string-templates": "2.2.12-alpha.52",
"@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",

View file

@ -1,12 +1,12 @@
{
"name": "@budibase/frontend-core",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase",
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "2.2.12-alpha.50",
"@budibase/bbui": "2.2.12-alpha.52",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/sdk",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase Public API SDK",
"author": "Budibase",
"license": "MPL-2.0",

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@ -43,11 +43,11 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.2.12-alpha.50",
"@budibase/client": "2.2.12-alpha.50",
"@budibase/pro": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/types": "2.2.12-alpha.50",
"@budibase/backend-core": "2.2.12-alpha.52",
"@budibase/client": "2.2.12-alpha.52",
"@budibase/pro": "2.2.12-alpha.52",
"@budibase/string-templates": "2.2.12-alpha.52",
"@budibase/types": "2.2.12-alpha.52",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",

View file

@ -1273,13 +1273,13 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.2.12-alpha.50.tgz#39f228904cf39de2dbb51900df4494267ac72381"
integrity sha512-ysVJ6Eul7o+LZRoilFr7bk1/gkoxSqM30yBbRhKxIhH2IOk0qd9LAaNQiTnwn4ZzU+tJWSkyAsJAQdTnX8/i9g==
"@budibase/backend-core@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.2.12-alpha.52.tgz#b1f7457f5aae71eaed4e4fb7ab3c2d760ea3d74a"
integrity sha512-JUpwI6U0b+n5CyatdUUDzYkVneJQWUbjkGHZiWZrP0TncrHeKi4HmBKPswYoEydzOQQOfpDOa7ZoK9ffEldf/A==
dependencies:
"@budibase/nano" "10.1.1"
"@budibase/types" "2.2.12-alpha.50"
"@budibase/types" "2.2.12-alpha.52"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
@ -1374,13 +1374,13 @@
qs "^6.11.0"
tough-cookie "^4.1.2"
"@budibase/pro@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.2.12-alpha.50.tgz#599edc75363b323f613a0d56e5173150582b13bb"
integrity sha512-eeKY9P4Zi81AlU6vYWVcffMwkyUrNv6PcNjdKZZNBuIgNfOVejy9vmpIukx43LpHUOsk6FKxHFNVDN1txnYRkg==
"@budibase/pro@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.2.12-alpha.52.tgz#37df13f60fbe873537e901f9183205100ddb8d98"
integrity sha512-Y5luiKVvkOtWEnfZ0lrqfNfclSyQrhFJnP9P7BgEBKUcqBguslaAObPZtHfcP1GXiR/I6hoZKXMCCSvHDRXFbg==
dependencies:
"@budibase/backend-core" "2.2.12-alpha.50"
"@budibase/types" "2.2.12-alpha.50"
"@budibase/backend-core" "2.2.12-alpha.52"
"@budibase/types" "2.2.12-alpha.52"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -1406,10 +1406,10 @@
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
"@budibase/types@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.2.12-alpha.50.tgz#ef07d4593c1ea0da6a6b75004d1319d7621cd910"
integrity sha512-LU+G7gQffBvnLvWfzpxUNcrCZFP/ZNQqAjB2KJxgq9pfeynU3MWAwut1tjKpp8eduuu7GnfBT1uvet9jM87FKA==
"@budibase/types@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.2.12-alpha.52.tgz#184c2c74a1705e0fad49ad14608b618db99ecabd"
integrity sha512-yQSxExP1l/kqy1grHB58STHDfs8UBwEozfRRs090kif3a69DfhAzur500EECW8otKahQKL+DUHedG0b/ilAVtw==
"@bull-board/api@3.7.0":
version "3.7.0"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/types",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase types",
"main": "dist/index.js",
"types": "dist/index.d.ts",

View file

@ -47,6 +47,7 @@ export interface User extends ThirdPartyUser {
account?: {
authType: string
}
onboardedAt?: string
}
export interface UserRoles {

View file

@ -6,6 +6,9 @@ export enum Event {
USER_UPDATED = "user:updated",
USER_DELETED = "user:deleted",
// USER / ONBOARDING
USER_ONBOARDING_COMPLETE = "user:onboarding:complete",
// USER / PERMISSIONS
USER_PERMISSION_ADMIN_ASSIGNED = "user:admin:assigned",
USER_PERMISSION_ADMIN_REMOVED = "user:admin:removed",

View file

@ -12,6 +12,11 @@ export interface UserDeletedEvent extends BaseEvent {
userId: string
}
export interface UserOnboardingEvent extends BaseEvent {
userId: string
step?: string
}
export interface UserPermissionAssignedEvent extends BaseEvent {
userId: string
}

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "2.2.12-alpha.50",
"version": "2.2.12-alpha.52",
"description": "Budibase background service",
"main": "src/index.ts",
"repository": {
@ -36,10 +36,10 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
"@budibase/backend-core": "2.2.12-alpha.50",
"@budibase/pro": "2.2.12-alpha.50",
"@budibase/string-templates": "2.2.12-alpha.50",
"@budibase/types": "2.2.12-alpha.50",
"@budibase/backend-core": "2.2.12-alpha.52",
"@budibase/pro": "2.2.12-alpha.52",
"@budibase/string-templates": "2.2.12-alpha.52",
"@budibase/types": "2.2.12-alpha.52",
"@koa/router": "8.0.8",
"@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2",

View file

@ -73,6 +73,10 @@ export const handleSaveEvents = async (
await events.user.permissionAdminRemoved(user)
}
if (isOnboardingComplete(user, existingUser)) {
await events.user.onboardingComplete(user)
}
if (
!existingUser.forceResetPassword &&
user.forceResetPassword &&
@ -114,6 +118,10 @@ const isRemovingAdmin = (user: any, existingUser: any) => {
return isRemovingPermission(user, existingUser, isAdmin)
}
const isOnboardingComplete = (user: any, existingUser: any) => {
return !existingUser?.onboardedAt && typeof user.onboardedAt === "string"
}
/**
* Check if a permission is being added to a new or existing user.
*/

View file

@ -470,13 +470,13 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.2.12-alpha.50.tgz#39f228904cf39de2dbb51900df4494267ac72381"
integrity sha512-ysVJ6Eul7o+LZRoilFr7bk1/gkoxSqM30yBbRhKxIhH2IOk0qd9LAaNQiTnwn4ZzU+tJWSkyAsJAQdTnX8/i9g==
"@budibase/backend-core@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.2.12-alpha.52.tgz#b1f7457f5aae71eaed4e4fb7ab3c2d760ea3d74a"
integrity sha512-JUpwI6U0b+n5CyatdUUDzYkVneJQWUbjkGHZiWZrP0TncrHeKi4HmBKPswYoEydzOQQOfpDOa7ZoK9ffEldf/A==
dependencies:
"@budibase/nano" "10.1.1"
"@budibase/types" "2.2.12-alpha.50"
"@budibase/types" "2.2.12-alpha.52"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
@ -521,13 +521,13 @@
qs "^6.11.0"
tough-cookie "^4.1.2"
"@budibase/pro@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.2.12-alpha.50.tgz#599edc75363b323f613a0d56e5173150582b13bb"
integrity sha512-eeKY9P4Zi81AlU6vYWVcffMwkyUrNv6PcNjdKZZNBuIgNfOVejy9vmpIukx43LpHUOsk6FKxHFNVDN1txnYRkg==
"@budibase/pro@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.2.12-alpha.52.tgz#37df13f60fbe873537e901f9183205100ddb8d98"
integrity sha512-Y5luiKVvkOtWEnfZ0lrqfNfclSyQrhFJnP9P7BgEBKUcqBguslaAObPZtHfcP1GXiR/I6hoZKXMCCSvHDRXFbg==
dependencies:
"@budibase/backend-core" "2.2.12-alpha.50"
"@budibase/types" "2.2.12-alpha.50"
"@budibase/backend-core" "2.2.12-alpha.52"
"@budibase/types" "2.2.12-alpha.52"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -535,10 +535,10 @@
lru-cache "^7.14.1"
node-fetch "^2.6.1"
"@budibase/types@2.2.12-alpha.50":
version "2.2.12-alpha.50"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.2.12-alpha.50.tgz#ef07d4593c1ea0da6a6b75004d1319d7621cd910"
integrity sha512-LU+G7gQffBvnLvWfzpxUNcrCZFP/ZNQqAjB2KJxgq9pfeynU3MWAwut1tjKpp8eduuu7GnfBT1uvet9jM87FKA==
"@budibase/types@2.2.12-alpha.52":
version "2.2.12-alpha.52"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.2.12-alpha.52.tgz#184c2c74a1705e0fad49ad14608b618db99ecabd"
integrity sha512-yQSxExP1l/kqy1grHB58STHDfs8UBwEozfRRs090kif3a69DfhAzur500EECW8otKahQKL+DUHedG0b/ilAVtw==
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"