add base wizard for datasources
This commit is contained in:
parent
c91e5ea39c
commit
0de6b55438
|
@ -1,74 +1,159 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@roxi/routify"
|
import { ModalContent, Modal, Body, Layout, Label } from "@budibase/bbui"
|
||||||
import { datasources } from "stores/backend"
|
import { onMount } from "svelte"
|
||||||
import { notifications } from "@budibase/bbui"
|
import ICONS from "../icons"
|
||||||
import { Input, Label, ModalContent, Modal, Context } from "@budibase/bbui"
|
import api from "builderStore/api"
|
||||||
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte"
|
import { IntegrationNames } from "constants"
|
||||||
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
|
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
|
||||||
import analytics from "analytics"
|
import DatasourceDetailsModal from "components/backend/DatasourceNavigator/modals/DatasourceDetailsModal.svelte"
|
||||||
import { getContext } from "svelte"
|
|
||||||
|
|
||||||
const modalContext = getContext(Context.Modal)
|
export let modal
|
||||||
|
let integrations = []
|
||||||
|
let integration = {}
|
||||||
|
let internalTableModal
|
||||||
|
let externalDatasourceModal
|
||||||
|
|
||||||
let tableModal
|
const INTERNAL = "BUDIBASE"
|
||||||
let name
|
|
||||||
let error = ""
|
|
||||||
let integration
|
|
||||||
|
|
||||||
$: checkOpenModal(integration && integration.type === "BUDIBASE")
|
onMount(() => {
|
||||||
|
fetchIntegrations()
|
||||||
|
})
|
||||||
|
|
||||||
function checkValid(evt) {
|
function selectIntegration(integrationType) {
|
||||||
const datasourceName = evt.target.value
|
const selected = integrations[integrationType]
|
||||||
if (
|
|
||||||
$datasources?.list.some(datasource => datasource.name === datasourceName)
|
// build the schema
|
||||||
) {
|
const config = {}
|
||||||
error = `Datasource with name ${datasourceName} already exists. Please choose another name.`
|
for (let key of Object.keys(selected.datasource)) {
|
||||||
return
|
config[key] = selected.datasource[key].default
|
||||||
}
|
}
|
||||||
error = ""
|
integration = {
|
||||||
}
|
type: integrationType,
|
||||||
|
plus: selected.plus,
|
||||||
function checkOpenModal(isInternal) {
|
|
||||||
if (isInternal) {
|
|
||||||
tableModal.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveDatasource() {
|
|
||||||
const { type, plus, ...config } = integration
|
|
||||||
|
|
||||||
// Create datasource
|
|
||||||
const response = await datasources.save({
|
|
||||||
name,
|
|
||||||
source: type,
|
|
||||||
config,
|
config,
|
||||||
plus,
|
schema: selected.datasource,
|
||||||
})
|
}
|
||||||
notifications.success(`Datasource ${name} created successfully.`)
|
}
|
||||||
analytics.captureEvent("Datasource Created", { name, type })
|
|
||||||
|
|
||||||
// Navigate to new datasource
|
function chooseNextModal() {
|
||||||
$goto(`./datasource/${response._id}`)
|
if (integration.type === INTERNAL) {
|
||||||
|
externalDatasourceModal.hide()
|
||||||
|
internalTableModal.show()
|
||||||
|
} else {
|
||||||
|
externalDatasourceModal.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchIntegrations() {
|
||||||
|
const response = await api.get("/api/integrations")
|
||||||
|
const json = await response.json()
|
||||||
|
integrations = {
|
||||||
|
[INTERNAL]: { datasource: {}, name: "INTERNAL/CSV" },
|
||||||
|
...json,
|
||||||
|
}
|
||||||
|
return json
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal bind:this={tableModal} on:hide={modalContext.hide}>
|
<Modal bind:this={internalTableModal}>
|
||||||
<CreateTableModal bind:name />
|
<CreateTableModal />
|
||||||
</Modal>
|
</Modal>
|
||||||
<ModalContent
|
|
||||||
title="Create Datasource"
|
<Modal bind:this={externalDatasourceModal}>
|
||||||
size="L"
|
<DatasourceDetailsModal {integration} />
|
||||||
confirmText="Create"
|
</Modal>
|
||||||
onConfirm={saveDatasource}
|
|
||||||
disabled={error || !name || !integration?.type}
|
<Modal bind:this={modal}>
|
||||||
>
|
<ModalContent
|
||||||
<Input
|
disabled={!Object.keys(integration).length}
|
||||||
data-cy="datasource-name-input"
|
title="Add Data"
|
||||||
label="Datasource Name"
|
confirmText="Continue"
|
||||||
on:input={checkValid}
|
cancelText="Start from scratch"
|
||||||
bind:value={name}
|
size="M"
|
||||||
{error}
|
onConfirm={() => {
|
||||||
/>
|
chooseNextModal()
|
||||||
<Label>Datasource Type</Label>
|
}}
|
||||||
<TableIntegrationMenu bind:integration />
|
>
|
||||||
</ModalContent>
|
<Body size="XS"
|
||||||
|
>All apps need data. You can connect to a data source below, or add data
|
||||||
|
to your app using Budibase's built-in database - it's simple!
|
||||||
|
</Body>
|
||||||
|
|
||||||
|
<Layout noPadding>
|
||||||
|
<div
|
||||||
|
class:selected={integration.type === INTERNAL}
|
||||||
|
on:click={() => selectIntegration(INTERNAL)}
|
||||||
|
class="item hoverable"
|
||||||
|
>
|
||||||
|
<div class="item-body">
|
||||||
|
<svelte:component this={ICONS.BUDIBASE} height="18" width="18" />
|
||||||
|
<span class="icon-spacing">
|
||||||
|
<Body size="S">Budibase DB (no prior data required)</Body></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Label size="S">Connect to data source</Label>
|
||||||
|
|
||||||
|
<div class="item-list">
|
||||||
|
{#each Object.entries(integrations).filter(([key]) => key !== INTERNAL) as [integrationType, schema]}
|
||||||
|
<div
|
||||||
|
class:selected={integration.type === integrationType}
|
||||||
|
on:click={() => selectIntegration(integrationType)}
|
||||||
|
class="item hoverable"
|
||||||
|
>
|
||||||
|
<div class="item-body">
|
||||||
|
<svelte:component
|
||||||
|
this={ICONS[integrationType]}
|
||||||
|
height="18"
|
||||||
|
width="18"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="icon-spacing">
|
||||||
|
<Body size="S"
|
||||||
|
>{schema.name || IntegrationNames[integrationType]}</Body
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon-spacing {
|
||||||
|
margin-left: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.item-body {
|
||||||
|
display: flex;
|
||||||
|
margin-left: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.item-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
grid-gap: var(--spectrum-alias-grid-baseline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
cursor: pointer;
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spectrum-alias-grid-margin-xsmall);
|
||||||
|
padding: var(--spectrum-alias-item-padding-s);
|
||||||
|
background: var(--spectrum-alias-background-color-secondary);
|
||||||
|
transition: 0.3s all;
|
||||||
|
border: solid var(--spectrum-alias-border-color);
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background: var(--spectrum-alias-background-color-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item:hover,
|
||||||
|
.selected {
|
||||||
|
background: var(--spectrum-alias-background-color-tertiary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<script>
|
||||||
|
import { ModalContent, notifications } from "@budibase/bbui"
|
||||||
|
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||||
|
import { datasources } from "stores/backend"
|
||||||
|
import { IntegrationNames } from "constants"
|
||||||
|
|
||||||
|
export let integration
|
||||||
|
|
||||||
|
function prepareData() {
|
||||||
|
let datasource = {}
|
||||||
|
let existingTypeCount = $datasources.list.filter(
|
||||||
|
ds => ds.type == integration.type
|
||||||
|
).length
|
||||||
|
|
||||||
|
let baseName = IntegrationNames[integration.type]
|
||||||
|
let name =
|
||||||
|
existingTypeCount == 0 ? baseName : `${baseName}-${existingTypeCount + 1}`
|
||||||
|
|
||||||
|
datasource.type = "datasource"
|
||||||
|
datasource.source = integration.type
|
||||||
|
datasource.config = integration.config
|
||||||
|
datasource.name = name
|
||||||
|
|
||||||
|
return datasource
|
||||||
|
}
|
||||||
|
async function saveDatasource() {
|
||||||
|
try {
|
||||||
|
// Create datasource
|
||||||
|
await datasources.save(prepareData())
|
||||||
|
notifications.success(`Datasource updated successfully.`)
|
||||||
|
} catch (err) {
|
||||||
|
notifications.error(`Error saving datasource: ${err}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<ModalContent
|
||||||
|
title="Add Data"
|
||||||
|
onConfirm={() => saveDatasource()}
|
||||||
|
confirmText="Continue"
|
||||||
|
cancelText="Start from scratch"
|
||||||
|
size="M"
|
||||||
|
>
|
||||||
|
<IntegrationConfigForm
|
||||||
|
schema={integration.schema}
|
||||||
|
bind:integration={integration.config}
|
||||||
|
/>
|
||||||
|
</ModalContent>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
|
@ -15,6 +15,20 @@ export const AppStatus = {
|
||||||
DEPLOYED: "published",
|
DEPLOYED: "published",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const IntegrationNames = {
|
||||||
|
POSTGRES: "PostgreSQL",
|
||||||
|
MONGODB: "MongoDB",
|
||||||
|
COUCHDB: "CouchDB",
|
||||||
|
S3: "S3",
|
||||||
|
MYSQL: "MySQL",
|
||||||
|
REST: "REST",
|
||||||
|
DYNAMODB: "DynamoDB",
|
||||||
|
ELASTICSEARCH: "ElasticSearch",
|
||||||
|
SQL_SERVER: "SQL Server",
|
||||||
|
AIRTABLE: "Airtable",
|
||||||
|
ARANGODB: "ArangoDB",
|
||||||
|
}
|
||||||
|
|
||||||
// fields on the user table that cannot be edited
|
// fields on the user table that cannot be edited
|
||||||
export const UNEDITABLE_USER_FIELDS = [
|
export const UNEDITABLE_USER_FIELDS = [
|
||||||
"email",
|
"email",
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto, params } from "@roxi/routify"
|
import { goto, params } from "@roxi/routify"
|
||||||
import { Icon, Modal, Tabs, Tab } from "@budibase/bbui"
|
import { Icon, Tabs, Tab } from "@budibase/bbui"
|
||||||
import { BUDIBASE_INTERNAL_DB } from "constants"
|
import { BUDIBASE_INTERNAL_DB } from "constants"
|
||||||
import DatasourceNavigator from "components/backend/DatasourceNavigator/DatasourceNavigator.svelte"
|
import DatasourceNavigator from "components/backend/DatasourceNavigator/DatasourceNavigator.svelte"
|
||||||
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
|
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
|
||||||
|
|
||||||
let selected = "Sources"
|
let selected = "Sources"
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
|
|
||||||
$: isExternal =
|
$: isExternal =
|
||||||
$params.selectedDatasource &&
|
$params.selectedDatasource &&
|
||||||
$params.selectedDatasource !== BUDIBASE_INTERNAL_DB
|
$params.selectedDatasource !== BUDIBASE_INTERNAL_DB
|
||||||
|
@ -23,9 +25,7 @@
|
||||||
<Tab title="Sources">
|
<Tab title="Sources">
|
||||||
<div class="tab-content-padding">
|
<div class="tab-content-padding">
|
||||||
<DatasourceNavigator />
|
<DatasourceNavigator />
|
||||||
<Modal bind:this={modal}>
|
<CreateDatasourceModal bind:modal />
|
||||||
<CreateDatasourceModal />
|
|
||||||
</Modal>
|
|
||||||
</div>
|
</div>
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@roxi/routify"
|
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
|
||||||
$goto("./table")
|
import { datasources } from "stores/backend"
|
||||||
|
|
||||||
|
$: setupComplete =
|
||||||
|
$datasources.list.find(x => (x._id = "bb_internal")).entities.length > 1 ||
|
||||||
|
$datasources.list > 1
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if !setupComplete}
|
||||||
|
<CreateDatasourceModal />
|
||||||
|
{/if}
|
||||||
<!-- routify:options index=false -->
|
<!-- routify:options index=false -->
|
||||||
|
|
Loading…
Reference in New Issue