forked from tsai/budibase
10 changed files with 313 additions and 4 deletions
@ -0,0 +1,59 @@ |
|||||
|
<script> |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
|
let dispatch = createEventDispatcher() |
||||
|
|
||||
|
export let type = "info" |
||||
|
export let icon = "Info" |
||||
|
export let size = "S" |
||||
|
export let extraButtonText |
||||
|
export let extraButtonAction |
||||
|
|
||||
|
let show = true |
||||
|
|
||||
|
function clear() { |
||||
|
show = false |
||||
|
dispatch("change") |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
{#if show} |
||||
|
<div class="spectrum-Toast spectrum-Toast--{type}"> |
||||
|
<svg |
||||
|
class="spectrum-Icon spectrum-Icon--size{size} spectrum-Toast-typeIcon" |
||||
|
focusable="false" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-icon-18-{icon}" /> |
||||
|
</svg> |
||||
|
<div class="spectrum-Toast-body"> |
||||
|
<div class="spectrum-Toast-content"> |
||||
|
<slot /> |
||||
|
</div> |
||||
|
{#if extraButtonText && extraButtonAction} |
||||
|
<button |
||||
|
class="spectrum-Button spectrum-Button--sizeM spectrum-Button--overBackground spectrum-Button--quiet" |
||||
|
on:click={extraButtonAction} |
||||
|
> |
||||
|
<span class="spectrum-Button-label">{extraButtonText}</span> |
||||
|
</button> |
||||
|
{/if} |
||||
|
</div> |
||||
|
<div class="spectrum-Toast-buttons"> |
||||
|
<button |
||||
|
class="spectrum-ClearButton spectrum-ClearButton--overBackground spectrum-ClearButton--size{size}" |
||||
|
on:click={clear} |
||||
|
> |
||||
|
<div class="spectrum-ClearButton-fill"> |
||||
|
<svg |
||||
|
class="spectrum-ClearButton-icon spectrum-Icon spectrum-UIIcon-Cross100" |
||||
|
focusable="false" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
<use xlink:href="#spectrum-css-icon-Cross100" /> |
||||
|
</svg> |
||||
|
</div> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
{/if} |
||||
@ -0,0 +1,63 @@ |
|||||
|
<script> |
||||
|
import { Heading, Icon, Input, Label, Body } from "@budibase/bbui" |
||||
|
import { createEventDispatcher } from "svelte" |
||||
|
|
||||
|
let dispatch = createEventDispatcher() |
||||
|
|
||||
|
export let defaultValue = "" |
||||
|
export let value |
||||
|
export let type = "label" |
||||
|
export let size = "M" |
||||
|
|
||||
|
let editing = false |
||||
|
|
||||
|
function setEditing(state) { |
||||
|
editing = state |
||||
|
if (editing) { |
||||
|
dispatch("change") |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="parent"> |
||||
|
{#if !editing} |
||||
|
{#if type === "heading"} |
||||
|
<Heading {size}>{value || defaultValue}</Heading> |
||||
|
{:else if type === "body"} |
||||
|
<Body {size}>{value || defaultValue}</Body> |
||||
|
{:else} |
||||
|
<Label {size}>{value || defaultValue}</Label> |
||||
|
{/if} |
||||
|
<div class="hide"> |
||||
|
<Icon name="Edit" hoverable size="S" on:click={() => setEditing(true)} /> |
||||
|
</div> |
||||
|
{:else} |
||||
|
<div class="input"> |
||||
|
<Input placeholder={defaultValue} bind:value on:change /> |
||||
|
</div> |
||||
|
<Icon |
||||
|
name="SaveFloppy" |
||||
|
hoverable |
||||
|
size="S" |
||||
|
on:click={() => setEditing(false)} |
||||
|
/> |
||||
|
{/if} |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.parent { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: var(--spacing-m); |
||||
|
} |
||||
|
.hide { |
||||
|
display: none; |
||||
|
margin-top: 5px; |
||||
|
} |
||||
|
.parent:hover .hide { |
||||
|
display: block; |
||||
|
} |
||||
|
.input { |
||||
|
flex: 1; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,4 @@ |
|||||
|
<script> |
||||
|
</script> |
||||
|
|
||||
|
<slot /> |
||||
@ -0,0 +1,173 @@ |
|||||
|
<script> |
||||
|
import { params } from "@roxi/routify" |
||||
|
import { datasources, integrations, queries } from "stores/backend" |
||||
|
import { |
||||
|
Layout, |
||||
|
Input, |
||||
|
Select, |
||||
|
Tabs, |
||||
|
Tab, |
||||
|
Banner, |
||||
|
Divider, |
||||
|
Button, |
||||
|
Heading, |
||||
|
} from "@budibase/bbui" |
||||
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" |
||||
|
import EditableLabel from "components/common/inputs/EditableLabel.svelte" |
||||
|
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte" |
||||
|
import { capitalise } from "helpers" |
||||
|
import { onMount } from "svelte" |
||||
|
|
||||
|
let query |
||||
|
let breakQs = {} |
||||
|
|
||||
|
$: datasource = $datasources.list.find(ds => ds._id === query?.datasourceId) |
||||
|
$: datasourceType = datasource?.source |
||||
|
$: integrationInfo = $integrations[datasourceType] |
||||
|
$: queryConfig = integrationInfo?.query |
||||
|
|
||||
|
function getSelectedQuery() { |
||||
|
return ( |
||||
|
$queries.list.find(q => q._id === $queries.selected) || { |
||||
|
datasourceId: $params.selectedDatasource, |
||||
|
parameters: [], |
||||
|
fields: {}, |
||||
|
queryVerb: "read", |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
function breakQueryString(qs) { |
||||
|
if (!qs) { |
||||
|
return {} |
||||
|
} |
||||
|
const params = qs.split("&") |
||||
|
let paramObj = {} |
||||
|
for (let param of params) { |
||||
|
const [key, value] = param.split("=") |
||||
|
paramObj[key] = value |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// function buildQueryString(obj) { |
||||
|
// let str = "" |
||||
|
// for (let [key, value] of Object.entries(obj)) { |
||||
|
// if (str !== "") { |
||||
|
// str += "&" |
||||
|
// } |
||||
|
// str += `${key}=${value}` |
||||
|
// } |
||||
|
// return str |
||||
|
// } |
||||
|
|
||||
|
function checkQueryName(queryToCheck, url = null) { |
||||
|
if (queryToCheck && (!queryToCheck.name || queryToCheck.flags.urlName)) { |
||||
|
queryToCheck.flags.urlName = true |
||||
|
queryToCheck.name = url || queryToCheck.fields.path |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function learnMoreBanner() {} |
||||
|
|
||||
|
function saveQuery() {} |
||||
|
|
||||
|
onMount(() => { |
||||
|
query = getSelectedQuery() |
||||
|
breakQs = breakQueryString(query?.fields.queryString) |
||||
|
if (query && !query.transformer) { |
||||
|
query.transformer = "return data" |
||||
|
} |
||||
|
if (query && !query.flags) { |
||||
|
query.flags = { |
||||
|
urlName: false, |
||||
|
bannerCleared: false, |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
{#if query} |
||||
|
<div class="inner"> |
||||
|
<div class="top"> |
||||
|
<Layout gap="S"> |
||||
|
<EditableLabel |
||||
|
type="heading" |
||||
|
bind:value={query.name} |
||||
|
defaultValue="Untitled" |
||||
|
on:change={() => (query.flags.urlName = false)} |
||||
|
/> |
||||
|
<div class="url-block"> |
||||
|
<div class="verb"> |
||||
|
<Select |
||||
|
bind:value={query.queryVerb} |
||||
|
on:change={() => {}} |
||||
|
options={Object.keys(queryConfig)} |
||||
|
getOptionLabel={verb => |
||||
|
queryConfig[verb]?.displayName || capitalise(verb)} |
||||
|
/> |
||||
|
</div> |
||||
|
<div class="url"> |
||||
|
<Input |
||||
|
bind:value={query.fields.path} |
||||
|
on:change={({ detail }) => checkQueryName(query, detail)} |
||||
|
/> |
||||
|
</div> |
||||
|
<Button cta disabled={!query.fields.path} on:click={saveQuery} |
||||
|
>Send</Button |
||||
|
> |
||||
|
</div> |
||||
|
<Tabs selected="Params"> |
||||
|
<Tab title="Params"> |
||||
|
<KeyValueBuilder bind:object={breakQs} name="param" /> |
||||
|
</Tab> |
||||
|
<Tab title="Headers" /> |
||||
|
<Tab title="Body" /> |
||||
|
<Tab title="Transformer"> |
||||
|
<Layout noPadding> |
||||
|
{#if !query.flags.bannerCleared} |
||||
|
<Banner |
||||
|
extraButtonText="Learn more" |
||||
|
extraButtonAction={learnMoreBanner} |
||||
|
on:change={() => (query.flags.bannerCleared = true)} |
||||
|
> |
||||
|
Add a JavaScript function to transform the query result. |
||||
|
</Banner> |
||||
|
{/if} |
||||
|
<CodeMirrorEditor |
||||
|
height={200} |
||||
|
value={query.transformer} |
||||
|
resize="vertical" |
||||
|
on:change={e => (query.transformer = e.detail)} |
||||
|
/> |
||||
|
</Layout> |
||||
|
</Tab> |
||||
|
</Tabs> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
<Layout paddingY="L"> |
||||
|
<Divider size="S" /> |
||||
|
<Heading size="M">Response</Heading> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
{/if} |
||||
|
|
||||
|
<style> |
||||
|
.inner { |
||||
|
width: 840px; |
||||
|
margin: 0 auto; |
||||
|
height: 100%; |
||||
|
} |
||||
|
.url-block { |
||||
|
display: flex; |
||||
|
gap: var(--spacing-s); |
||||
|
} |
||||
|
.verb { |
||||
|
flex: 1; |
||||
|
} |
||||
|
.url { |
||||
|
flex: 4; |
||||
|
} |
||||
|
.top { |
||||
|
min-height: 50%; |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue