diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte deleted file mode 100644 index 290f25b941..0000000000 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - {#if integration?.auth?.type === "google"} - - {:else} - - {/if} - - - - {#if integration.type === "REST"} - modal.show()} - /> - {/if} - - - - showImportModal()} - showCancelButton={false} - size="M" - onConfirm={() => { - chooseNextModal() - }} - > - - Get started with Budibase DB -
selectIntegration(IntegrationTypes.INTERNAL)} - class="item hoverable" - > -
- -
- Budibase DB - Non-relational -
-
-
-
- - - Connect to an external datasource -
- {#each sortedIntegrations.filter(([key, val]) => key !== IntegrationTypes.INTERNAL && !val.custom) as [integrationType, schema]} - selectIntegration(evt.detail)} - {schema} - bind:integrationType - {integration} - /> - {/each} -
-
- - {#if customIntegrations.length > 0} - - Custom datasource -
- {#each customIntegrations as [integrationType, schema]} - selectIntegration(evt.detail)} - {schema} - bind:integrationType - {integration} - /> - {/each} -
-
- {/if} -
-
- - diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte index e75109b1be..21c7e07a25 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/DatasourceConfigModal.svelte @@ -11,7 +11,6 @@ import { DatasourceFeature } from "@budibase/types" export let integration - export let modal // kill the reference so the input isn't saved let datasource = cloneDeep(integration) @@ -62,7 +61,6 @@ saveDatasource()} - onCancel={() => modal.show()} confirmText={datasource.plus ? "Connect" : "Save and continue to query"} cancelText="Back" showSecondaryButton={datasource.plus} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte index de9ecce778..0783a9fe53 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/GoogleDatasourceConfigModal.svelte @@ -8,7 +8,6 @@ import { onMount } from "svelte" export let integration - export let modal // kill the reference so the input isn't saved let datasource = cloneDeep(integration) @@ -21,7 +20,6 @@ modal.show()} cancelText="Back" size="L" > diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 8d8418eb81..f34a3e9c98 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -4,6 +4,7 @@ import { API } from "api" import { parseFile } from "./utils" + let fileInput let error = null let fileName = null let fileType = null @@ -16,6 +17,7 @@ export let schema = {} export let allValid = true export let displayColumn = null + export let promptUpload = false const typeOptions = [ { @@ -99,10 +101,19 @@ schema[name].type = e.detail schema[name].constraints = FIELDS[e.detail.toUpperCase()].constraints } + + const openFileUpload = (promptUpload, fileInput) => { + if (promptUpload && fileInput) { + fileInput.click() + } + } + + $: openFileUpload(promptUpload, fileInput)
{} export let afterSave = async table => { @@ -136,7 +137,13 @@ - +
diff --git a/packages/builder/src/components/common/FontAwesomeIcon.svelte b/packages/builder/src/components/common/FontAwesomeIcon.svelte index 84c16abeda..364b3af25f 100644 --- a/packages/builder/src/components/common/FontAwesomeIcon.svelte +++ b/packages/builder/src/components/common/FontAwesomeIcon.svelte @@ -8,6 +8,7 @@ faLock, faFileArrowUp, faChevronLeft, + faCircleInfo, } from "@fortawesome/free-solid-svg-icons" import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons" @@ -20,7 +21,8 @@ faDiscord, faEnvelope, faFileArrowUp, - faChevronLeft + faChevronLeft, + faCircleInfo ) dom.watch() diff --git a/packages/builder/src/components/portal/onboarding/TourPopover.svelte b/packages/builder/src/components/portal/onboarding/TourPopover.svelte index 68e2e68a49..d4958b386e 100644 --- a/packages/builder/src/components/portal/onboarding/TourPopover.svelte +++ b/packages/builder/src/components/portal/onboarding/TourPopover.svelte @@ -71,6 +71,9 @@ tourStep.onComplete() } popover.hide() + if (tourStep.endRoute) { + $goto(tourStep.endRoute) + } } } diff --git a/packages/builder/src/components/portal/onboarding/tours.js b/packages/builder/src/components/portal/onboarding/tours.js index 3ca25b7481..ed7776fc2d 100644 --- a/packages/builder/src/components/portal/onboarding/tours.js +++ b/packages/builder/src/components/portal/onboarding/tours.js @@ -76,6 +76,7 @@ const getTours = () => { 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) diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 62194df3db..9e055cd798 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -1,12 +1,6 @@ + +
+
+
+ +
+ {title} +
+ +
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte index 8b401866f5..87c4db81df 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte @@ -1,21 +1,20 @@
- - - - - - - + {#if !$isActive("./new")} + + + + + + + {/if}
diff --git a/packages/builder/src/pages/builder/app/[application]/data/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/index.svelte index b2aca1f7f3..47939f09b4 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/index.svelte @@ -1,22 +1,17 @@ - - diff --git a/packages/builder/src/pages/builder/app/[application]/data/new.svelte b/packages/builder/src/pages/builder/app/[application]/data/new.svelte new file mode 100644 index 0000000000..f8e8fd85e7 --- /dev/null +++ b/packages/builder/src/pages/builder/app/[application]/data/new.svelte @@ -0,0 +1,257 @@ + + + + + + + + {#if integration?.auth?.type === "google"} + + {:else} + + {/if} + + +
+
+ {#if hasData} + + {/if} +
+
+ Add new data source +
+ +
+ Get started with our Budibase DB + +
+ +
+ + + + + + + + + +
+ +
+ Or connect to an external datasource +
+ +
+ {#each integrations as [key, value]} + handleIntegrationSelect(key)} + title={value.friendlyName} + description={value.type} + {disabled} + > + + + {/each} +
+
+ + diff --git a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DataPanel.svelte b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DataPanel.svelte deleted file mode 100644 index 9a7fffd893..0000000000 --- a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DataPanel.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -
- - -
diff --git a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DatasourceConfigPanel.svelte b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DatasourceConfigPanel.svelte deleted file mode 100644 index 2b44648279..0000000000 --- a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/DatasourceConfigPanel.svelte +++ /dev/null @@ -1,120 +0,0 @@ - - -
- -
- - {#each Object.entries(fields) as [name, { type, default: defaultValue, required }]} - {#if type !== "boolean"} - {}} - label={formatName(name)} - {type} - /> - {/if} - {/each} - {#each Object.entries(fields) as [name, { type, default: defaultValue, required }]} - {#if type === "boolean"} - - {/if} - {/each} - -
- {#if isGoogle} - - {:else} - - {/if} -
- - diff --git a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/ExampleApp.svelte b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/ExampleApp.svelte index 3e970ac360..0b290decbf 100644 --- a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/ExampleApp.svelte +++ b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/ExampleApp.svelte @@ -1,6 +1,5 @@ - - - -
- {#if stage === "name"} - (stage = "data")} /> - {:else if googleComplete} -
- Please login to your Google account in the new tab which as opened to - continue. -
- {:else if integrationsLoading || creationLoading} -
- -
- {:else if stage === "data"} - (stage = "name")}> -
- handleCreateApp({ useSampleData: true })} - > -
-
- -
- Budibase Sample data -
-
-
-
- -
-
- -
- Upload data (CSV or JSON) -
-
-
- {#each Object.entries(plusIntegrations) as [integrationType, schema]} -
- (stage = integrationType)}> -
-
- -
- {schema.friendlyName} -
-
-
- {/each} -
- {:else if stage in plusIntegrations} - (stage = "data")} - onNext={data => { - const isGoogle = data.isGoogle - delete data.isGoogle - return handleCreateApp({ datasourceConfig: data, isGoogle }) - }} - /> - {:else} -

There was an problem. Please refresh the page and try again.

- {/if} +
- +
@@ -258,35 +77,4 @@ .full-width { width: 100%; } - .centered { - display: flex; - justify-content: center; - align-items: center; - min-height: 400px; - } - - .dataButton { - margin-bottom: 12px; - } - - .dataButtonContent { - display: flex; - align-items: center; - } - - .budibaseLogo { - height: 20px; - } - - .dataButtonIcon { - width: 22px; - display: flex; - justify-content: center; - margin-right: 16px; - } - - .dataButtonContent :global(svg) { - font-size: 18px; - color: white; - } diff --git a/packages/builder/src/stores/backend/tables.js b/packages/builder/src/stores/backend/tables.js index ba900b7df9..a36c91d1b1 100644 --- a/packages/builder/src/stores/backend/tables.js +++ b/packages/builder/src/stores/backend/tables.js @@ -63,9 +63,7 @@ export function createTablesStore() { const savedTable = await API.saveTable(updatedTable) replaceTable(table._id, savedTable) - if (table.type === "external") { - await datasources.fetch() - } + await datasources.fetch() select(savedTable._id) return savedTable } diff --git a/packages/frontend-core/src/api/app.js b/packages/frontend-core/src/api/app.js index 7868eef063..ce18bcc0c5 100644 --- a/packages/frontend-core/src/api/app.js +++ b/packages/frontend-core/src/api/app.js @@ -152,4 +152,10 @@ export const buildAppEndpoints = API => ({ url: `/api/${appId}/components/definitions`, }) }, + + addSampleData: async appId => { + return await API.post({ + url: `/api/applications/${appId}/sample`, + }) + }, }) diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 815366785c..9c89b48b8a 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -26,7 +26,10 @@ import { env as envCore, } from "@budibase/backend-core" import { USERS_TABLE_SCHEMA } from "../../constants" -import { buildDefaultDocs } from "../../db/defaultData/datasource_bb_default" +import { + DEFAULT_BB_DATASOURCE_ID, + buildDefaultDocs, +} from "../../db/defaultData/datasource_bb_default" import { removeAppFromUserRoles } from "../../utilities/workerRequests" import { stringToReadStream, isQsTrue } from "../../utilities" import { getLocksById, doesUserHaveLock } from "../../utilities/redis" @@ -111,11 +114,7 @@ function checkAppName( } } -async function createInstance( - appId: string, - template: any, - includeSampleData: boolean -) { +async function createInstance(appId: string, template: any) { const db = context.getAppDB() await db.put({ _id: "_design/database", @@ -142,21 +141,25 @@ async function createInstance( } else { // create the users table await db.put(USERS_TABLE_SCHEMA) - - if (includeSampleData) { - // create ootb stock db - await addDefaultTables(db) - } } return { _id: appId } } -async function addDefaultTables(db: Database) { - const defaultDbDocs = buildDefaultDocs() +export const addSampleData = async (ctx: UserCtx) => { + const db = context.getAppDB() - // add in the default db data docs - tables, datasource, rows and links - await db.bulkDocs([...defaultDbDocs]) + try { + // Check if default datasource exists before creating it + await sdk.datasources.get(DEFAULT_BB_DATASOURCE_ID) + } catch (err: any) { + const defaultDbDocs = buildDefaultDocs() + + // add in the default db data docs - tables, datasource, rows and links + await db.bulkDocs([...defaultDbDocs]) + } + + ctx.status = 200 } export async function fetch(ctx: UserCtx) { @@ -248,16 +251,11 @@ async function performAppCreate(ctx: UserCtx) { if (ctx.request.files && ctx.request.files.templateFile) { instanceConfig.file = ctx.request.files.templateFile } - const includeSampleData = isQsTrue(ctx.request.body.sampleData) const tenantId = tenancy.isMultiTenant() ? tenancy.getTenantId() : null const appId = generateDevAppID(generateAppID(tenantId)) return await context.doInAppContext(appId, async () => { - const instance = await createInstance( - appId, - instanceConfig, - includeSampleData - ) + const instance = await createInstance(appId, instanceConfig) const db = context.getAppDB() let newApplication: App = { diff --git a/packages/server/src/api/routes/application.ts b/packages/server/src/api/routes/application.ts index 0aa88568f3..0c1fa364ff 100644 --- a/packages/server/src/api/routes/application.ts +++ b/packages/server/src/api/routes/application.ts @@ -38,6 +38,11 @@ router authorized(permissions.BUILDER), controller.revertClient ) + .post( + "/api/applications/:appId/sample", + authorized(permissions.BUILDER), + controller.addSampleData + ) .post( "/api/applications/:appId/publish", authorized(permissions.BUILDER),