diff --git a/lerna.json b/lerna.json index cc0b07a99e..256258d6b1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.3.8", + "version": "0.4.2", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 94faa124a8..e92034c120 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -115,22 +115,22 @@ Cypress.Commands.add("createUser", (email, password, role) => { // Create User cy.contains("Users").click() - cy.contains("Create New Row").click() + cy.contains("Create New User").click() cy.get(".modal").within(() => { cy.get("input") .first() - .type(password) + .type(email) cy.get("input") .eq(1) - .type(email) + .type(password) cy.get("select") .first() .select(role) // Save cy.get(".buttons") - .contains("Create Row") + .contains("Create User") .click() }) }) diff --git a/packages/builder/package.json b/packages/builder/package.json index 542c35205e..6c023efd29 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.3.8", + "version": "0.4.2", "license": "AGPL-3.0", "private": true, "scripts": { @@ -64,7 +64,7 @@ }, "dependencies": { "@budibase/bbui": "^1.52.2", - "@budibase/client": "^0.3.8", + "@budibase/client": "^0.4.2", "@budibase/colorpicker": "^1.0.1", "@budibase/svelte-ag-grid": "^0.0.16", "@fortawesome/fontawesome-free": "^5.14.0", diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index 887ef733e4..cf668670bb 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -2,9 +2,9 @@ import { getFrontendStore } from "./store/frontend" import { getBackendUiStore } from "./store/backend" import { getAutomationStore } from "./store/automation/" import { getThemeStore } from "./store/theme" -import { derived } from "svelte/store" +import { derived, writable } from "svelte/store" import analytics from "analytics" -import { LAYOUT_NAMES } from "../constants" +import { FrontendTypes, LAYOUT_NAMES } from "../constants" import { makePropsSafe } from "components/userInterface/assetParsing/createProps" export const store = getFrontendStore() @@ -13,18 +13,12 @@ export const automationStore = getAutomationStore() export const themeStore = getThemeStore() export const currentAsset = derived(store, $store => { - const layout = $store.layouts - ? $store.layouts.find(layout => layout._id === $store.currentAssetId) - : null - - if (layout) return layout - - const screen = $store.screens - ? $store.screens.find(screen => screen._id === $store.currentAssetId) - : null - - if (screen) return screen - + const type = $store.currentFrontEndType + if (type === FrontendTypes.SCREEN) { + return $store.screens.find(screen => screen._id === $store.selectedScreenId) + } else if (type === FrontendTypes.LAYOUT) { + return $store.layouts.find(layout => layout._id === $store.selectedLayoutId) + } return null }) @@ -59,8 +53,14 @@ export const selectedComponent = derived( } ) -export const currentAssetName = derived(store, () => { - return currentAsset.name +export const currentAssetId = derived(store, $store => { + return $store.currentFrontEndType === FrontendTypes.SCREEN + ? $store.selectedScreenId + : $store.selectedLayoutId +}) + +export const currentAssetName = derived(currentAsset, $currentAsset => { + return $currentAsset?.name }) // leave this as before for consistency @@ -74,6 +74,8 @@ export const mainLayout = derived(store, $store => { ) }) +export const selectedAccessRole = writable("BASIC") + export const initialise = async () => { try { await analytics.activate() diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js index 3cdabf1ed5..a1e3cfffdb 100644 --- a/packages/builder/src/builderStore/store/backend.js +++ b/packages/builder/src/builderStore/store/backend.js @@ -1,11 +1,14 @@ import { writable, get } from "svelte/store" import { cloneDeep } from "lodash/fp" import api from "../api" +import { backendUiStore } from ".." const INITIAL_BACKEND_UI_STATE = { tables: [], views: [], users: [], + roles: [], + datasources: [], selectedDatabase: {}, selectedTable: {}, draftTable: {}, @@ -20,9 +23,12 @@ export const getBackendUiStore = () => { select: async db => { const tablesResponse = await api.get(`/api/tables`) const tables = await tablesResponse.json() + const datasourcesResponse = await api.get(`/api/datasources`) + const datasources = await datasourcesResponse.json() store.update(state => { state.selectedDatabase = db state.tables = tables + state.datasources = datasources return state }) }, @@ -44,6 +50,69 @@ export const getBackendUiStore = () => { return state }), }, + datasources: { + fetch: async () => { + const response = await api.get(`/api/datasources`) + const json = await response.json() + store.update(state => { + state.datasources = json + return state + }) + return json + }, + select: async datasource => { + store.update(state => { + state.selectedDatasourceId = datasource._id + return state + }) + }, + save: async datasource => { + const response = await api.post("/api/datasources", datasource) + const json = await response.json() + store.update(state => { + const currentIdx = state.datasources.findIndex( + ds => ds._id === json._id + ) + + if (currentIdx >= 0) { + state.datasources.splice(currentIdx, 1, json) + } else { + state.datasources.push(json) + } + + state.datasources = state.datasources + return state + }) + }, + saveQuery: async (datasourceId, query) => { + const response = await api.post( + `/api/datasources/${datasourceId}/queries`, + query + ) + const json = await response.json() + store.update(state => { + const currentIdx = state.datasources.findIndex( + ds => ds._id === json._id + ) + + if (currentIdx >= 0) { + state.datasources.splice(currentIdx, 1, json) + } else { + state.datasources.push(json) + } + + state.datasources = state.datasources + return state + }) + }, + }, + queries: { + select: queryId => + store.update(state => { + state.selectedQueryId = queryId + return state + }), + }, tables: { fetch: async () => { const tablesResponse = await api.get(`/api/tables`) @@ -177,6 +246,26 @@ export const getBackendUiStore = () => { return state }), }, + roles: { + fetch: async () => { + const response = await api.get("/api/roles") + const roles = await response.json() + store.update(state => { + state.roles = roles + return state + }) + }, + delete: async role => { + const response = await api.delete(`/api/roles/${role._id}/${role._rev}`) + await store.actions.roles.fetch() + return response + }, + save: async role => { + const response = await api.post("/api/roles", role) + await store.actions.roles.fetch() + return response + }, + }, } return store diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 5bb10a24c2..263041aadd 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -3,7 +3,6 @@ import { cloneDeep } from "lodash/fp" import { createProps, getBuiltin, - makePropsSafe, } from "components/userInterface/assetParsing/createProps" import { allScreens, @@ -11,6 +10,7 @@ import { currentAsset, mainLayout, selectedComponent, + selectedAccessRole, } from "builderStore" import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import api from "../api" @@ -32,7 +32,8 @@ const INITIAL_FRONTEND_STATE = { screens: [], components: [], currentFrontEndType: "none", - currentAssetId: "", + selectedScreenId: "", + selectedLayoutId: "", selectedComponentId: "", errors: [], hasAppPackage: false, @@ -83,28 +84,31 @@ export const getFrontendStore = () => { }, }, screens: { - select: async screenId => { - let promise + select: screenId => { store.update(state => { - const screen = get(allScreens).find(screen => screen._id === screenId) + let screens = get(allScreens) + let screen = + screens.find(screen => screen._id === screenId) || screens[0] if (!screen) return state - state.currentFrontEndType = FrontendTypes.SCREEN - state.currentAssetId = screenId - state.currentView = "detail" + // Update role to the screen's role setting so that it will always + // be visible + selectedAccessRole.set(screen.routing.roleId) - promise = store.actions.screens.regenerateCss(screen) + state.currentFrontEndType = FrontendTypes.SCREEN + state.selectedScreenId = screen._id + state.currentView = "detail" state.selectedComponentId = screen.props?._id return state }) - await promise }, create: async screen => { screen = await store.actions.screens.save(screen) store.update(state => { - state.currentAssetId = screen._id + state.selectedScreenId = screen._id state.selectedComponentId = screen.props._id state.currentFrontEndType = FrontendTypes.SCREEN + selectedAccessRole.set(screen.routing.roleId) return state }) return screen @@ -113,6 +117,7 @@ export const getFrontendStore = () => { const creatingNewScreen = screen._id === undefined const response = await api.post(`/api/screens`, screen) screen = await response.json() + await store.actions.routing.fetch() store.update(state => { const foundScreen = state.screens.findIndex( @@ -122,28 +127,14 @@ export const getFrontendStore = () => { state.screens.splice(foundScreen, 1) } state.screens.push(screen) - - if (creatingNewScreen) { - const safeProps = makePropsSafe( - state.components[screen.props._component], - screen.props - ) - state.selectedComponentId = safeProps._id - screen.props = safeProps - } return state }) - return screen - }, - regenerateCss: async asset => { - const response = await api.post("/api/css/generate", asset) - asset._css = (await response.json())?.css - }, - regenerateCssForCurrentScreen: async () => { - const asset = get(currentAsset) - if (asset) { - await store.actions.screens.regenerateCss(asset) + + if (creatingNewScreen) { + store.actions.screens.select(screen._id) } + + return screen }, delete: async screens => { const screensToDelete = Array.isArray(screens) ? screens : [screens] @@ -159,8 +150,8 @@ export const getFrontendStore = () => { `/api/screens/${screenToDelete._id}/${screenToDelete._rev}` ) ) - if (screenToDelete._id === state.currentAssetId) { - state.currentAssetId = "" + if (screenToDelete._id === state.selectedScreenId) { + state.selectedScreenId = null } } return state @@ -181,50 +172,44 @@ export const getFrontendStore = () => { }, }, layouts: { - select: async layoutId => { + select: layoutId => { store.update(state => { - const layout = store.actions.layouts.find(layoutId) - + const layout = + store.actions.layouts.find(layoutId) || get(store).layouts[0] + if (!layout) return state.currentFrontEndType = FrontendTypes.LAYOUT state.currentView = "detail" - - state.currentAssetId = layout._id + state.selectedLayoutId = layout._id state.selectedComponentId = layout.props?._id - return state }) - let cssPromises = [] - cssPromises.push(store.actions.screens.regenerateCssForCurrentScreen()) - - for (let screen of get(allScreens)) { - cssPromises.push(store.actions.screens.regenerateCss(screen)) - } - await Promise.all(cssPromises) }, save: async layout => { const layoutToSave = cloneDeep(layout) - delete layoutToSave._css - + const creatingNewLayout = layoutToSave._id === undefined const response = await api.post(`/api/layouts`, layoutToSave) - - const json = await response.json() + const savedLayout = await response.json() store.update(state => { const layoutIdx = state.layouts.findIndex( - stateLayout => stateLayout._id === json._id + stateLayout => stateLayout._id === savedLayout._id ) - if (layoutIdx >= 0) { // update existing layout - state.layouts.splice(layoutIdx, 1, json) + state.layouts.splice(layoutIdx, 1, savedLayout) } else { // save new layout - state.layouts.push(json) + state.layouts.push(savedLayout) } - - state.currentAssetId = json._id return state }) + + // Select layout if creating a new one + if (creatingNewLayout) { + store.actions.layouts.select(savedLayout._id) + } + + return savedLayout }, find: layoutId => { if (!layoutId) { @@ -237,16 +222,17 @@ export const getFrontendStore = () => { const response = await api.delete( `/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}` ) - if (response.status !== 200) { const json = await response.json() throw new Error(json.message) } - store.update(state => { state.layouts = state.layouts.filter( layout => layout._id !== layoutToDelete._id ) + if (layoutToDelete._id === state.selectedLayoutId) { + state.selectedLayoutId = get(mainLayout)._id + } return state }) }, @@ -372,7 +358,6 @@ export const getFrontendStore = () => { const index = mode === "above" ? targetIndex : targetIndex + 1 parent._children.splice(index, 0, cloneDeep(componentToPaste)) - promises.push(store.actions.screens.regenerateCssForCurrentScreen()) promises.push(store.actions.preview.saveSelected()) store.actions.components.select(componentToPaste) @@ -390,8 +375,6 @@ export const getFrontendStore = () => { } selected._styles[type][name] = value - promises.push(store.actions.screens.regenerateCssForCurrentScreen()) - // save without messing with the store promises.push(store.actions.preview.saveSelected()) return state @@ -476,13 +459,8 @@ export const getFrontendStore = () => { }).props } - // Save layout and regenerate all CSS because otherwise weird things happen + // Save layout nav._children = [...nav._children, newLink] - state.currentAssetId = layout._id - promises.push(store.actions.screens.regenerateCss(layout)) - for (let screen of get(allScreens)) { - promises.push(store.actions.screens.regenerateCss(screen)) - } promises.push(store.actions.layouts.save(layout)) } return state diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 063ca34633..436a3b4dee 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -4,12 +4,17 @@ import CreateColumnButton from "./buttons/CreateColumnButton.svelte" import CreateViewButton from "./buttons/CreateViewButton.svelte" import ExportButton from "./buttons/ExportButton.svelte" + import EditRolesButton from "./buttons/EditRolesButton.svelte" import * as api from "./api" import Table from "./Table.svelte" + import { TableNames } from "constants" + import CreateEditUser from "./modals/CreateEditUser.svelte" + import CreateEditRow from "./modals/CreateEditRow.svelte" let data = [] let loading = false + $: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS $: title = $backendUiStore.selectedTable.name $: schema = $backendUiStore.selectedTable.schema $: tableView = { @@ -29,11 +34,22 @@ } - +
{#if schema && Object.keys(schema).length > 0} - + {/if} + {#if isUsersTable} + + {/if}
diff --git a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte index 33d107e1c5..d89165a4c7 100644 --- a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte @@ -1,32 +1,63 @@ +{#if error} +
{error}
+{/if} - +
+ + diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 8b1c2e18e6..bddb66e4c9 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -7,20 +7,16 @@ Toggle, RichText, } from "@budibase/bbui" - import { backendUiStore } from "builderStore" - import { TableNames } from "constants" import Dropzone from "components/common/Dropzone.svelte" import { capitalise } from "../../../helpers" import LinkedRowSelector from "components/common/LinkedRowSelector.svelte" export let meta - export let creating export let value = meta.type === "boolean" ? false : "" + export let readonly $: type = meta.type $: label = capitalise(meta.name) - $: editingUser = - !creating && $backendUiStore.selectedTable?._id === TableNames.USERS {#if type === 'options'} @@ -53,5 +49,5 @@ data-cy="{meta.name}-input" {type} bind:value - disabled={editingUser} /> + disabled={readonly} /> {/if} diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 2599e4f8b0..1e6c53bd11 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -7,10 +7,15 @@ import { notifier } from "builderStore/store/notifications" import Spinner from "components/common/Spinner.svelte" import DeleteRowsButton from "./buttons/DeleteRowsButton.svelte" - import { getRenderer, editRowRenderer } from "./cells/cellRenderers" + import { + getRenderer, + editRowRenderer, + userRowRenderer, + } from "./cells/cellRenderers" import TableLoadingOverlay from "./TableLoadingOverlay" import TableHeader from "./TableHeader" import "@budibase/svelte-ag-grid/dist/index.css" + import { TableNames } from "constants" export let schema = {} export let data = [] @@ -42,7 +47,18 @@ animateRows: true, } + $: isUsersTable = tableId === TableNames.USERS $: { + if (isUsersTable) { + schema.email.displayFieldName = "Email" + schema.roleId.displayFieldName = "Role" + } + } + + $: { + // Reset selection every time data changes + selectedRows = [] + let result = [] if (allowEditing) { result = [ @@ -57,23 +73,34 @@ suppressMenu: true, minWidth: 114, width: 114, - cellRenderer: editRowRenderer, + cellRenderer: isUsersTable ? userRowRenderer : editRowRenderer, }, ] } - Object.keys(schema || {}).forEach((key, idx) => { + const canEditColumn = key => { + if (!allowEditing) { + return false + } + return !(isUsersTable && ["email", "roleId"].includes(key)) + } + + Object.entries(schema || {}).forEach(([key, value]) => { result.push({ headerCheckboxSelection: false, headerComponent: TableHeader, headerComponentParams: { field: schema[key], - editable: allowEditing, + editable: canEditColumn(key), }, - headerName: key, + headerName: value.displayFieldName || key, field: key, sortable: true, - cellRenderer: getRenderer(schema[key], true), + cellRenderer: getRenderer({ + schema: schema[key], + editable: true, + isUsersTable, + }), cellRendererParams: { selectRelationship, }, diff --git a/packages/builder/src/components/backend/DataTable/api.js b/packages/builder/src/components/backend/DataTable/api.js index 629405a9fc..e28f8d9fa6 100644 --- a/packages/builder/src/components/backend/DataTable/api.js +++ b/packages/builder/src/components/backend/DataTable/api.js @@ -23,5 +23,22 @@ export async function fetchDataForView(view) { const FETCH_ROWS_URL = `/api/views/${view.name}` const response = await api.get(FETCH_ROWS_URL) - return await response.json() + const json = await response.json() + + if (response.status !== 200) { + throw new Error(json.message) + } + return json +} + +export async function fetchDataForQuery(datasourceId, queryId) { + const FETCH_QUERY_URL = `/api/datasources/${datasourceId}/queries/${queryId}` + + const response = await api.get(FETCH_QUERY_URL) + const json = await response.json() + + if (response.status !== 200) { + throw new Error(json.message) + } + return json } diff --git a/packages/builder/src/components/backend/DataTable/buttons/CreateQueryButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/CreateQueryButton.svelte new file mode 100644 index 0000000000..d2f3ed0c57 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/CreateQueryButton.svelte @@ -0,0 +1,47 @@ + + +
+ +
+ + + + + diff --git a/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte index 3c0444881f..19975ac1a1 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/CreateRowButton.svelte @@ -1,6 +1,9 @@ @@ -8,9 +11,9 @@
- + diff --git a/packages/builder/src/components/backend/DataTable/buttons/EditIntegrationSchemaButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/EditIntegrationSchemaButton.svelte new file mode 100644 index 0000000000..b75818f75d --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/EditIntegrationSchemaButton.svelte @@ -0,0 +1,41 @@ + + +
+ +
+ + + + + diff --git a/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte new file mode 100644 index 0000000000..024905fddc --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/buttons/EditRolesButton.svelte @@ -0,0 +1,23 @@ + + +
+ +
+ + + + + diff --git a/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte new file mode 100644 index 0000000000..a45f376a3e --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/cells/RoleCell.svelte @@ -0,0 +1,10 @@ + + +
{roleName}
diff --git a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js index 209f23119f..15a7910186 100644 --- a/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js +++ b/packages/builder/src/components/backend/DataTable/cells/cellRenderers.js @@ -1,16 +1,25 @@ import AttachmentList from "./AttachmentCell.svelte" import EditRow from "../modals/EditRow.svelte" +import CreateEditUser from "../modals/CreateEditUser.svelte" import DeleteRow from "../modals/DeleteRow.svelte" import RelationshipDisplay from "./RelationshipCell.svelte" +import RoleCell from "./RoleCell.svelte" const renderers = { attachment: attachmentRenderer, link: linkedRowRenderer, } -export function getRenderer(schema, editable) { +export function getRenderer({ schema, editable, isUsersTable }) { + const rendererParams = { + options: schema.options, + constraints: schema.constraints, + editable, + } if (renderers[schema.type]) { - return renderers[schema.type](schema.options, schema.constraints, editable) + return renderers[schema.type](rendererParams) + } else if (isUsersTable && schema.name === "roleId") { + return roleRenderer(rendererParams) } else { return false } @@ -45,15 +54,31 @@ export function editRowRenderer(params) { return container } -/* eslint-disable no-unused-vars */ -function attachmentRenderer(options, constraints, editable) { +export function userRowRenderer(params) { + const container = document.createElement("div") + container.style.height = "100%" + container.style.display = "flex" + container.style.alignItems = "center" + + new EditRow({ + target: container, + props: { + row: params.data, + modalContentComponent: CreateEditUser, + }, + }) + + return container +} + +function attachmentRenderer() { return params => { const container = document.createElement("div") container.style.height = "100%" container.style.display = "flex" container.style.alignItems = "center" - const attachmentInstance = new AttachmentList({ + new AttachmentList({ target: container, props: { files: params.value || [], @@ -64,7 +89,6 @@ function attachmentRenderer(options, constraints, editable) { } } -/* eslint-disable no-unused-vars */ function linkedRowRenderer() { return params => { let container = document.createElement("div") @@ -84,3 +108,21 @@ function linkedRowRenderer() { return container } } + +function roleRenderer() { + return params => { + let container = document.createElement("div") + container.style.display = "grid" + container.style.height = "100%" + container.style.alignItems = "center" + + new RoleCell({ + target: container, + props: { + roleId: params.value, + }, + }) + + return container + } +} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditQuery.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditQuery.svelte new file mode 100644 index 0000000000..e62a9336ac --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditQuery.svelte @@ -0,0 +1,190 @@ + + +
+
+
Datasource Type
+ {datasource.source} + + + + + + + + + + + + + + + + + + + + + + {#if preview} + + {#if previewTab === 'PREVIEW'} +
{JSON.stringify(preview, undefined, 2)}
+ {:else if previewTab === 'SCHEMA'} + {#each fields as field, idx} +
+ + + deleteField(idx)} /> +
+ {/each} + + {/if} +
+ {/if} +
+
+ + diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte similarity index 81% rename from packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte rename to packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte index e99c5762d8..050c7ce200 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditRowModal.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte @@ -1,6 +1,5 @@ + + + + + + + + {#each customSchemaKeys as [key, meta]} + + {/each} + diff --git a/packages/builder/src/components/backend/DataTable/modals/EditIntegrationConfig.svelte b/packages/builder/src/components/backend/DataTable/modals/EditIntegrationConfig.svelte index 9becd0593f..dfd7c11141 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditIntegrationConfig.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditIntegrationConfig.svelte @@ -7,11 +7,14 @@ Heading, Spacer, } from "@budibase/bbui" + import { notifier } from "builderStore/store/notifications" import { FIELDS } from "constants/backend" import { backendUiStore } from "builderStore" + import * as api from "../api" export let table + let smartSchemaRow let fields = Object.keys(table.schema).map(field => ({ name: field, type: table.schema[field].type.toUpperCase(), @@ -34,11 +37,29 @@ fields.splice(idx, 1) fields = fields } + + async function smartSchema() { + try { + const rows = await api.fetchDataForView($backendUiStore.selectedView) + const first = rows[0] + smartSchemaRow = first + fields = Object.keys(first).map(key => ({ + // TODO: Smarter type mapping + name: key, + type: "STRING", + })) + } catch (err) { + notifier.danger("Error determining schema. Please enter fields manually.") + } + }
Schema
+ {#if smartSchemaRow} +
{JSON.stringify(smartSchemaRow, undefined, 2)}
+ {/if} {#each fields as field, idx}
@@ -55,6 +76,7 @@
{/each} +
@@ -102,5 +124,6 @@ .delete { align-self: center; + cursor: pointer; } diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte new file mode 100644 index 0000000000..3b97f6d61b --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -0,0 +1,132 @@ + + + + {#if errors.length} + + {/if} + + {#if selectedRole} + + + + {/if} +
+ {#if !isCreating} + + {/if} +
+
diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte index a4d2a74fc2..5d858a50f4 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRow.svelte @@ -1,8 +1,9 @@ + +{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id} +
+ +
+
+ {#each $backendUiStore.datasources as datasource, idx} + 0} + icon={'ri-database-2-line'} + text={datasource.name} + selected={$backendUiStore.selectedDatasourceId === datasource._id} + on:click={() => selectDatasource(datasource)}> + + + {#each Object.keys(datasource.queries) as queryId} + onClickQuery(datasource._id, queryId)}> + + + {/each} + {/each} +
+{/if} + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte new file mode 100644 index 0000000000..f148eaf8b2 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte @@ -0,0 +1,55 @@ + + +
+ + {title} + +
+ + diff --git a/packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte similarity index 74% rename from packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte rename to packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte index 913e26f668..e7ce7aea49 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte @@ -1,5 +1,5 @@ @@ -11,5 +11,6 @@ type={configKey.type} label={configKey} bind:value={integration[configKey]} /> + {/each} diff --git a/packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/index.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte similarity index 69% rename from packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/index.svelte rename to packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte index 80d096f440..d04b569fe1 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableIntegrationMenu/index.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte @@ -1,6 +1,6 @@ + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte new file mode 100644 index 0000000000..cc22870ec2 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte @@ -0,0 +1,57 @@ + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte new file mode 100644 index 0000000000..d72ca668b1 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte @@ -0,0 +1,89 @@ + + +
+
+ +
+ + + + + +
+ + Are you sure you wish to delete the datasource + {datasource.name}? + This action cannot be undone. + + + diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index 6af63a66f9..6c67e44ae2 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -2,11 +2,10 @@ import { goto } from "@sveltech/routify" import { backendUiStore } from "builderStore" import { TableNames } from "constants" - import ListItem from "./ListItem.svelte" import CreateTableModal from "./modals/CreateTableModal.svelte" import EditTablePopover from "./popovers/EditTablePopover.svelte" import EditViewPopover from "./popovers/EditViewPopover.svelte" - import { Modal } from "@budibase/bbui" + import { Modal, Switcher } from "@budibase/bbui" import NavItem from "components/common/NavItem.svelte" let modal @@ -37,7 +36,6 @@ {#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
-

Tables

@@ -48,7 +46,9 @@ text={table.name} selected={selectedView === `all_${table._id}`} on:click={() => selectTable(table)}> - + {#if table._id !== TableNames.USERS} + + {/if} {#each Object.keys(table.views || {}) as viewName}
- - -
-
- - -
+ + diff --git a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte index 05302788cb..3aba583ccb 100644 --- a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte +++ b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte @@ -3,7 +3,6 @@ import { notifier } from "builderStore/store/notifications" import { DropdownMenu, Button, Input } from "@budibase/bbui" import ConfirmDialog from "components/common/ConfirmDialog.svelte" - import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" export let table @@ -79,9 +78,6 @@ bind:value={table.name} on:input={checkValid} {error} /> - {#if table.integration?.type} - - {/if}