Merge pull request #10584 from Budibase/feature/db-query-save-prompt

Database query save prompt
This commit is contained in:
deanhannigan 2023-05-31 15:21:36 +01:00 committed by GitHub
commit 8784773f49
5 changed files with 120 additions and 29 deletions

View File

@ -3,6 +3,7 @@
export let query = {} export let query = {}
export let data = [] export let data = []
export let editRows = false
let loading = false let loading = false
let error = false let error = false
@ -12,7 +13,14 @@
{#if error} {#if error}
<div class="errors">{error}</div> <div class="errors">{error}</div>
{/if} {/if}
<Table schema={query.schema} {data} {loading} {type} rowCount={5} /> <Table
schema={query.schema}
{data}
{loading}
{type}
rowCount={5}
allowEditing={editRows}
/>
<style> <style>
.errors { .errors {

View File

@ -22,8 +22,8 @@
export let rowCount export let rowCount
export let disableSorting = false export let disableSorting = false
export let customPlaceholder = false export let customPlaceholder = false
export let allowClickRows
export let allowEditing = true export let allowEditing = true
export let allowClickRows
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()

View File

@ -9,6 +9,8 @@
import { store } from "builderStore" import { store } from "builderStore"
import { API } from "api" import { API } from "api"
export let disabled = false
let revertModal let revertModal
let appName let appName
@ -34,6 +36,7 @@
size="M" size="M"
tooltip="Revert changes" tooltip="Revert changes"
on:click={revertModal.show} on:click={revertModal.show}
{disabled}
/> />
<Modal bind:this={revertModal}> <Modal bind:this={revertModal}>

View File

@ -1,5 +1,5 @@
<script> <script>
import { goto } from "@roxi/routify" import { goto, beforeUrlChange } from "@roxi/routify"
import { import {
Icon, Icon,
Select, Select,
@ -12,6 +12,8 @@
Heading, Heading,
Tabs, Tabs,
Tab, Tab,
Modal,
ModalContent,
} from "@budibase/bbui" } from "@budibase/bbui"
import { notifications, Divider } from "@budibase/bbui" import { notifications, Divider } from "@budibase/bbui"
import ExtraQueryConfig from "./ExtraQueryConfig.svelte" import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
@ -29,6 +31,12 @@
export let query export let query
const resumeNavigation = () => {
if (typeof navigateTo == "string") {
$goto(typeof navigateTo == "string" ? `${navigateTo}` : navigateTo)
}
}
const transformerDocs = "https://docs.budibase.com/docs/transformers" const transformerDocs = "https://docs.budibase.com/docs/transformers"
let fields = query?.schema ? schemaToFields(query.schema) : [] let fields = query?.schema ? schemaToFields(query.schema) : []
@ -36,6 +44,31 @@
let data = [] let data = []
let saveId let saveId
let currentTab = "JSON" let currentTab = "JSON"
let saveModal
let override = false
let navigateTo = null
// seed the transformer
if (query && !query.transformer) {
query.transformer = "return data"
}
// initialise a new empty schema
if (query && !query.schema) {
query.schema = {}
}
let queryStr = JSON.stringify(query)
$beforeUrlChange(event => {
const updated = JSON.stringify(query)
if (updated !== queryStr && !override) {
navigateTo = event.type == "pushstate" ? event.url : null
saveModal.show()
return false
} else return true
})
$: datasource = $datasources.list.find(ds => ds._id === query.datasourceId) $: datasource = $datasources.list.find(ds => ds._id === query.datasourceId)
$: query.schema = fieldsToSchema(fields) $: query.schema = fieldsToSchema(fields)
@ -60,11 +93,6 @@
} }
} }
// seed the transformer
if (query && !query.transformer) {
query.transformer = "return data"
}
function resetDependentFields() { function resetDependentFields() {
if (query.fields.extra) { if (query.fields.extra) {
query.fields.extra = {} query.fields.extra = {}
@ -101,22 +129,48 @@
} }
} }
// return the query.
async function saveQuery() { async function saveQuery() {
try { try {
const { _id } = await queries.save(query.datasourceId, query) const response = await queries.save(query.datasourceId, query)
saveId = _id saveId = response._id
notifications.success(`Query saved successfully`)
// Go to the correct URL if we just created a new query if (response?._rev) {
if (!query._rev) { queryStr = JSON.stringify(query)
$goto(`../../${_id}`)
} }
return response
} catch (error) { } catch (error) {
notifications.error("Error saving query") notifications.error("Error saving query")
} }
} }
</script> </script>
<Modal
bind:this={saveModal}
on:hide={() => {
navigateTo = null
}}
>
<ModalContent
title="You have unsaved changes"
confirmText="Save and Continue"
cancelText="Discard Changes"
size="L"
onConfirm={async () => {
await saveQuery()
override = true
resumeNavigation()
}}
onCancel={async () => {
override = true
resumeNavigation()
}}
>
<Body>Leaving this section will mean losing and changes to your query</Body>
</ModalContent>
</Modal>
<div class="wrapper"> <div class="wrapper">
<Layout gap="S" noPadding> <Layout gap="S" noPadding>
<Heading size="M">Query {integrationInfo?.friendlyName}</Heading> <Heading size="M">Query {integrationInfo?.friendlyName}</Heading>
@ -125,7 +179,13 @@
<div class="config"> <div class="config">
<div class="config-field"> <div class="config-field">
<Label>Query Name</Label> <Label>Query Name</Label>
<Input bind:value={query.name} /> <Input
value={query.name}
on:input={e => {
let newValue = e.target.value || ""
query.name = newValue.trim()
}}
/>
</div> </div>
{#if queryConfig} {#if queryConfig}
<div class="config-field"> <div class="config-field">
@ -149,18 +209,20 @@
/> />
{/if} {/if}
{#key query.parameters} {#key query.parameters}
<BindingBuilder <div class="binding-wrap">
queryBindings={query.parameters} <BindingBuilder
bindable={false} queryBindings={query.parameters}
on:change={e => { bindable={false}
query.parameters = e.detail.map(binding => { on:change={e => {
return { query.parameters = e.detail.map(binding => {
name: binding.name, return {
default: binding.value, name: binding.name,
} default: binding.value,
}) }
}} })
/> }}
/>
</div>
{/key} {/key}
{/if} {/if}
</div> </div>
@ -203,7 +265,18 @@
<div class="viewer-controls"> <div class="viewer-controls">
<Heading size="S">Results</Heading> <Heading size="S">Results</Heading>
<ButtonGroup gap="XS"> <ButtonGroup gap="XS">
<Button cta disabled={queryInvalid} on:click={saveQuery}> <Button
cta
disabled={queryInvalid}
on:click={async () => {
await saveQuery()
notifications.success(`Query saved successfully`)
// Go to the correct URL if we just created a new query
if (!query._rev) {
$goto(`../../${query._id}`)
}
}}
>
Save Query Save Query
</Button> </Button>
<Button secondary on:click={previewQuery}>Run Query</Button> <Button secondary on:click={previewQuery}>Run Query</Button>
@ -274,4 +347,9 @@
min-width: 150px; min-width: 150px;
align-items: center; align-items: center;
} }
.binding-wrap :global(div.container) {
padding-left: 0px;
padding-right: 0px;
}
</style> </style>

View File

@ -3,8 +3,10 @@
import QueryViewer from "components/integration/QueryViewer.svelte" import QueryViewer from "components/integration/QueryViewer.svelte"
import RestQueryViewer from "components/integration/RestQueryViewer.svelte" import RestQueryViewer from "components/integration/RestQueryViewer.svelte"
import { IntegrationTypes } from "constants/backend" import { IntegrationTypes } from "constants/backend"
import { cloneDeep } from "lodash/fp"
$: query = $queries.selected $: query = $queries.selected
$: editableQuery = cloneDeep(query)
$: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId) $: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId)
$: isRestQuery = datasource?.source === IntegrationTypes.REST $: isRestQuery = datasource?.source === IntegrationTypes.REST
</script> </script>
@ -13,6 +15,6 @@
{#if isRestQuery} {#if isRestQuery}
<RestQueryViewer queryId={$queries.selectedQueryId} /> <RestQueryViewer queryId={$queries.selectedQueryId} />
{:else} {:else}
<QueryViewer {query} /> <QueryViewer query={editableQuery} />
{/if} {/if}
{/if} {/if}