mirror of https://github.com/Budibase/budibase.git
committed by
GitHub
234 changed files with 7700 additions and 12096 deletions
File diff suppressed because it is too large
@ -1,13 +0,0 @@ |
|||
import { Screen } from "./utils/Screen" |
|||
|
|||
export default { |
|||
name: `New Row (Empty)`, |
|||
create: () => createScreen(), |
|||
} |
|||
|
|||
const createScreen = () => { |
|||
return new Screen() |
|||
.component("@budibase/standard-components/newrow") |
|||
.table("") |
|||
.json() |
|||
} |
|||
@ -1,13 +0,0 @@ |
|||
import { Screen } from "./utils/Screen" |
|||
|
|||
export default { |
|||
name: `Row Detail (Empty)`, |
|||
create: () => createScreen(), |
|||
} |
|||
|
|||
const createScreen = () => { |
|||
return new Screen() |
|||
.component("@budibase/standard-components/rowdetail") |
|||
.table("") |
|||
.json() |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
import { TableNames } from "../constants" |
|||
import { |
|||
AUTO_COLUMN_DISPLAY_NAMES, |
|||
AUTO_COLUMN_SUB_TYPES, |
|||
FIELDS, |
|||
isAutoColumnUserRelationship, |
|||
} from "../constants/backend" |
|||
|
|||
export function getAutoColumnInformation(enabled = true) { |
|||
let info = {} |
|||
for (let [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) { |
|||
info[subtype] = { enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key] } |
|||
} |
|||
return info |
|||
} |
|||
|
|||
export function buildAutoColumn(tableName, name, subtype) { |
|||
let type, constraints |
|||
switch (subtype) { |
|||
case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: |
|||
case AUTO_COLUMN_SUB_TYPES.CREATED_BY: |
|||
type = FIELDS.LINK.type |
|||
constraints = FIELDS.LINK.constraints |
|||
break |
|||
case AUTO_COLUMN_SUB_TYPES.AUTO_ID: |
|||
type = FIELDS.NUMBER.type |
|||
constraints = FIELDS.NUMBER.constraints |
|||
break |
|||
case AUTO_COLUMN_SUB_TYPES.UPDATED_AT: |
|||
case AUTO_COLUMN_SUB_TYPES.CREATED_AT: |
|||
type = FIELDS.DATETIME.type |
|||
constraints = FIELDS.DATETIME.constraints |
|||
break |
|||
default: |
|||
type = FIELDS.STRING.type |
|||
constraints = FIELDS.STRING.constraints |
|||
break |
|||
} |
|||
if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) { |
|||
throw "Cannot build auto column with supplied subtype" |
|||
} |
|||
const base = { |
|||
name, |
|||
type, |
|||
subtype, |
|||
icon: "ri-magic-line", |
|||
autocolumn: true, |
|||
constraints, |
|||
} |
|||
if (isAutoColumnUserRelationship(subtype)) { |
|||
base.tableId = TableNames.USERS |
|||
base.fieldName = `${tableName}-${name}` |
|||
} |
|||
return base |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
<script> |
|||
import { TextButton } from "@budibase/bbui" |
|||
|
|||
export let hideAutocolumns |
|||
|
|||
let anchor |
|||
let dropdown |
|||
|
|||
function hideOrUnhide() { |
|||
hideAutocolumns = !hideAutocolumns |
|||
} |
|||
</script> |
|||
|
|||
<div bind:this={anchor}> |
|||
<TextButton text small on:click={hideOrUnhide}> |
|||
{#if hideAutocolumns} |
|||
<i class="ri-magic-line" /> |
|||
Show Auto Columns |
|||
{:else}<i class="ri-magic-fill" /> Hide Auto Columns{/if} |
|||
</TextButton> |
|||
</div> |
|||
|
|||
<style> |
|||
i { |
|||
margin-right: 4px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,43 @@ |
|||
<script> |
|||
import { TextButton, Icon, Popover } from "@budibase/bbui" |
|||
import { backendUiStore } from "builderStore" |
|||
import { Roles } from "constants/backend" |
|||
import api from "builderStore/api" |
|||
import ManageAccessPopover from "../popovers/ManageAccessPopover.svelte" |
|||
|
|||
export let resourceId |
|||
|
|||
let anchor |
|||
let dropdown |
|||
let levels |
|||
let permissions |
|||
|
|||
async function openDropdown() { |
|||
permissions = await backendUiStore.actions.permissions.forResource( |
|||
resourceId |
|||
) |
|||
levels = await backendUiStore.actions.permissions.fetchLevels() |
|||
dropdown.show() |
|||
} |
|||
</script> |
|||
|
|||
<div bind:this={anchor}> |
|||
<TextButton text small on:click={openDropdown}> |
|||
<i class="ri-lock-line" /> |
|||
Manage Access |
|||
</TextButton> |
|||
</div> |
|||
<Popover bind:this={dropdown} {anchor} align="left"> |
|||
<ManageAccessPopover |
|||
{resourceId} |
|||
{levels} |
|||
{permissions} |
|||
onClosed={dropdown.hide} /> |
|||
</Popover> |
|||
|
|||
<style> |
|||
i { |
|||
margin-right: var(--spacing-xs); |
|||
font-size: var(--font-size-s); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,94 @@ |
|||
<script> |
|||
import { onMount } from "svelte" |
|||
import { backendUiStore } from "builderStore" |
|||
import { Roles } from "constants/backend" |
|||
import api from "builderStore/api" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { Button, Label, Input, Select, Spacer } from "@budibase/bbui" |
|||
|
|||
export let resourceId |
|||
export let permissions |
|||
export let onClosed |
|||
|
|||
async function changePermission(level, role) { |
|||
await backendUiStore.actions.permissions.save({ |
|||
level, |
|||
role, |
|||
resource: resourceId, |
|||
}) |
|||
|
|||
// Show updated permissions in UI: REMOVE |
|||
permissions = await backendUiStore.actions.permissions.forResource( |
|||
resourceId |
|||
) |
|||
notifier.success("Updated permissions.") |
|||
// TODO: update permissions |
|||
// permissions[] |
|||
} |
|||
</script> |
|||
|
|||
<div class="popover"> |
|||
<h5>Who Can Access This Data?</h5> |
|||
<div class="note"> |
|||
<Label extraSmall grey> |
|||
Specify the minimum access level role for this data. |
|||
</Label> |
|||
</div> |
|||
<Spacer large /> |
|||
<div class="row"> |
|||
<Label extraSmall grey>Level</Label> |
|||
<Label extraSmall grey>Role</Label> |
|||
{#each Object.keys(permissions) as level} |
|||
<Input secondary thin value={level} disabled={true} /> |
|||
<Select |
|||
secondary |
|||
thin |
|||
value={permissions[level]} |
|||
on:change={e => changePermission(level, e.target.value)}> |
|||
{#each $backendUiStore.roles as role} |
|||
<option value={role._id}>{role.name}</option> |
|||
{/each} |
|||
</Select> |
|||
{/each} |
|||
</div> |
|||
|
|||
<Spacer large /> |
|||
|
|||
<div class="footer"> |
|||
<Button secondary on:click={onClosed}>Cancel</Button> |
|||
</div> |
|||
</div> |
|||
|
|||
<style> |
|||
.popover { |
|||
display: grid; |
|||
width: 400px; |
|||
} |
|||
|
|||
h5 { |
|||
margin: 0; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
hr { |
|||
margin: var(--spacing-s) 0 var(--spacing-m) 0; |
|||
} |
|||
|
|||
.footer { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
gap: var(--spacing-m); |
|||
margin-top: var(--spacing-l); |
|||
} |
|||
|
|||
.row { |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr; |
|||
grid-gap: var(--spacing-m); |
|||
} |
|||
|
|||
.note { |
|||
margin-top: 10px; |
|||
margin-bottom: 0; |
|||
} |
|||
</style> |
|||
@ -1,15 +1,38 @@ |
|||
<script> |
|||
import { Input, TextArea, Spacer } from "@budibase/bbui" |
|||
import { Label, Input, TextArea, Spacer } from "@budibase/bbui" |
|||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" |
|||
|
|||
export let integration |
|||
export let schema |
|||
|
|||
let unsaved = false |
|||
</script> |
|||
|
|||
<form> |
|||
{#each Object.keys(integration) as configKey} |
|||
<Input |
|||
type={integration[configKey].type} |
|||
label={configKey} |
|||
bind:value={integration[configKey]} /> |
|||
<Spacer large /> |
|||
{#each Object.keys(schema) as configKey} |
|||
{#if typeof schema[configKey].type === 'object'} |
|||
<Label small>{configKey}</Label> |
|||
<Spacer small /> |
|||
<KeyValueBuilder bind:object={integration[configKey]} on:change /> |
|||
{:else} |
|||
<div class="form-row"> |
|||
<Label small>{configKey}</Label> |
|||
<Input |
|||
outline |
|||
type={schema[configKey].type} |
|||
on:change |
|||
bind:value={integration[configKey]} /> |
|||
</div> |
|||
{/if} |
|||
{/each} |
|||
</form> |
|||
|
|||
<style> |
|||
.form-row { |
|||
display: grid; |
|||
grid-template-columns: 20% 1fr; |
|||
grid-gap: var(--spacing-l); |
|||
align-items: center; |
|||
margin-bottom: var(--spacing-m); |
|||
} |
|||
</style> |
|||
|
|||
@ -0,0 +1,36 @@ |
|||
<script> |
|||
export let width = "100" |
|||
export let height = "100" |
|||
</script> |
|||
|
|||
<svg |
|||
{width} |
|||
{height} |
|||
viewBox="0 0 120 120" |
|||
fill="none" |
|||
xmlns="http://www.w3.org/2000/svg"> |
|||
<path |
|||
d="M103.125 20.625H68.25L60.375 |
|||
36.375H16.5V99.375H111V20.625H103.125ZM103.125 36.375H72.375L76.5 |
|||
28.5H103.125V36.375Z" |
|||
fill="#FFBA58" /> |
|||
<path |
|||
d="M75 46.875V52.5H60C58.0127 52.5059 56.1085 53.298 54.7033 54.7033C53.298 |
|||
56.1085 52.5059 58.0127 52.5 60V75H46.875C44.3886 75 42.004 75.9877 40.2459 |
|||
77.7459C38.4877 79.504 37.5 81.8886 37.5 84.375C37.5 86.8614 38.4877 89.246 |
|||
40.2459 91.0041C42.004 92.7623 44.3886 93.75 46.875 |
|||
93.75H52.5V108.75C52.5059 110.737 53.298 112.642 54.7033 114.047C56.1085 |
|||
115.452 58.0127 116.244 60 116.25H74.25V110.625C74.25 107.94 75.3167 105.364 |
|||
77.2155 103.466C79.1143 101.567 81.6897 100.5 84.375 100.5C87.0603 100.5 |
|||
89.6357 101.567 91.5345 103.466C93.4333 105.364 94.5 107.94 94.5 |
|||
110.625V116.25H108.75C110.737 116.244 112.642 115.452 114.047 |
|||
114.047C115.452 112.642 116.244 110.737 116.25 108.75V94.5H110.625C107.94 |
|||
94.5 105.364 93.4333 103.466 91.5345C101.567 89.6357 100.5 87.0603 100.5 |
|||
84.375C100.5 81.6897 101.567 79.1143 103.466 77.2155C105.364 75.3167 107.94 |
|||
74.25 110.625 74.25H116.25V60C116.244 58.0127 115.452 56.1085 114.047 |
|||
54.7033C112.642 53.298 110.737 52.5059 108.75 52.5H93.75V46.875C93.75 |
|||
44.3886 92.7623 42.004 91.0041 40.2459C89.246 38.4877 86.8614 37.5 84.375 |
|||
37.5C81.8886 37.5 79.504 38.4877 77.7459 40.2459C75.9877 42.004 75 44.3886 |
|||
75 46.875Z" |
|||
fill="#E76A00" /> |
|||
</svg> |
|||
@ -1,61 +0,0 @@ |
|||
<script> |
|||
import { goto, params } from "@sveltech/routify" |
|||
import { backendUiStore, store } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { Input, Label, ModalContent, Button, Spacer } from "@budibase/bbui" |
|||
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte" |
|||
import analytics from "analytics" |
|||
|
|||
let modal |
|||
let error = "" |
|||
|
|||
let name |
|||
let source |
|||
let integration |
|||
let datasource |
|||
|
|||
function checkValid(evt) { |
|||
const datasourceName = evt.target.value |
|||
if ( |
|||
$backendUiStore.datasources?.some( |
|||
datasource => datasource.name === datasourceName |
|||
) |
|||
) { |
|||
error = `Datasource with name ${tableName} already exists. Please choose another name.` |
|||
return |
|||
} |
|||
error = "" |
|||
} |
|||
|
|||
async function saveDatasource() { |
|||
const { type, ...config } = integration |
|||
|
|||
// Create datasource |
|||
await backendUiStore.actions.datasources.save({ |
|||
name, |
|||
source: type, |
|||
config, |
|||
}) |
|||
notifier.success(`Datasource ${name} created successfully.`) |
|||
analytics.captureEvent("Datasource Created", { name }) |
|||
|
|||
// Navigate to new datasource |
|||
$goto(`./datasource/${datasource._id}`) |
|||
} |
|||
</script> |
|||
|
|||
<ModalContent |
|||
title="Create Datasource" |
|||
confirmText="Create" |
|||
onConfirm={saveDatasource} |
|||
disabled={error || !name}> |
|||
<Input |
|||
data-cy="datasource-name-input" |
|||
thin |
|||
label="Datasource Name" |
|||
on:input={checkValid} |
|||
bind:value={name} |
|||
{error} /> |
|||
<Label grey extraSmall>Create Integrated Table from External Source</Label> |
|||
<TableIntegrationMenu bind:integration /> |
|||
</ModalContent> |
|||
@ -1,39 +0,0 @@ |
|||
<script> |
|||
import { backendUiStore, store, allScreens } from "builderStore" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui" |
|||
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
|||
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte" |
|||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
|||
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" |
|||
|
|||
export let bindable |
|||
export let parameters |
|||
|
|||
let anchor |
|||
let dropdown |
|||
let confirmDeleteDialog |
|||
|
|||
function hideEditor() { |
|||
dropdown?.hide() |
|||
} |
|||
</script> |
|||
|
|||
<div on:click|stopPropagation bind:this={anchor}> |
|||
<TextButton text on:click={dropdown.show} active={false}> |
|||
<Icon name="add" /> |
|||
Add Parameters |
|||
</TextButton> |
|||
<DropdownMenu align="right" {anchor} bind:this={dropdown}> |
|||
<div class="wrapper"> |
|||
<ParameterBuilder bind:parameters {bindable} /> |
|||
</div> |
|||
</DropdownMenu> |
|||
</div> |
|||
|
|||
<style> |
|||
.wrapper { |
|||
padding: var(--spacing-xl); |
|||
min-width: 600px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,86 @@ |
|||
<script> |
|||
import { Icon, Input, Drawer, Body, Button } from "@budibase/bbui" |
|||
import { |
|||
readableToRuntimeBinding, |
|||
runtimeToReadableBinding, |
|||
} from "builderStore/dataBinding" |
|||
import BindingPanel from "components/design/PropertiesPanel/BindingPanel.svelte" |
|||
import { createEventDispatcher } from "svelte" |
|||
const dispatch = createEventDispatcher() |
|||
|
|||
export let value = "" |
|||
export let bindings = [] |
|||
export let thin = true |
|||
export let title = "Bindings" |
|||
export let placeholder |
|||
|
|||
let bindingDrawer |
|||
|
|||
$: tempValue = value |
|||
$: readableValue = runtimeToReadableBinding(bindings, value) |
|||
|
|||
const handleClose = () => { |
|||
onChange(tempValue) |
|||
bindingDrawer.hide() |
|||
} |
|||
|
|||
const onChange = value => { |
|||
dispatch("change", readableToRuntimeBinding(bindings, value)) |
|||
} |
|||
</script> |
|||
|
|||
<div class="control"> |
|||
<Input |
|||
{thin} |
|||
value={readableValue} |
|||
on:change={event => onChange(event.target.value)} |
|||
{placeholder} /> |
|||
<div class="icon" on:click={bindingDrawer.show}> |
|||
<Icon name="lightning" /> |
|||
</div> |
|||
</div> |
|||
<Drawer bind:this={bindingDrawer} {title}> |
|||
<div slot="description"> |
|||
<Body extraSmall grey> |
|||
Add the objects on the left to enrich your text. |
|||
</Body> |
|||
</div> |
|||
<heading slot="buttons"> |
|||
<Button thin blue on:click={handleClose}>Save</Button> |
|||
</heading> |
|||
<div slot="body"> |
|||
<BindingPanel |
|||
value={readableValue} |
|||
close={handleClose} |
|||
on:update={event => (tempValue = event.detail)} |
|||
bindableProperties={bindings} /> |
|||
</div> |
|||
</Drawer> |
|||
|
|||
<style> |
|||
.control { |
|||
flex: 1; |
|||
position: relative; |
|||
} |
|||
|
|||
.icon { |
|||
right: 2px; |
|||
top: 2px; |
|||
bottom: 2px; |
|||
position: absolute; |
|||
align-items: center; |
|||
display: flex; |
|||
box-sizing: border-box; |
|||
padding-left: 7px; |
|||
border-left: 1px solid var(--grey-4); |
|||
background-color: var(--grey-2); |
|||
border-top-right-radius: var(--border-radius-m); |
|||
border-bottom-right-radius: var(--border-radius-m); |
|||
color: var(--grey-7); |
|||
font-size: 14px; |
|||
} |
|||
.icon:hover { |
|||
color: var(--ink); |
|||
cursor: pointer; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="attachment" /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="boolean" /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="datetime" /> |
|||
@ -1,207 +0,0 @@ |
|||
<script> |
|||
import { TextButton, Body, DropdownMenu, ModalContent } from "@budibase/bbui" |
|||
import { AddIcon, ArrowDownIcon } from "components/common/Icons/" |
|||
import actionTypes from "./actions" |
|||
import { createEventDispatcher } from "svelte" |
|||
import { automationStore } from "builderStore" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
const eventTypeKey = "##eventHandlerType" |
|||
|
|||
export let event |
|||
|
|||
let addActionButton |
|||
let addActionDropdown |
|||
let selectedAction |
|||
|
|||
$: actions = event || [] |
|||
$: selectedActionComponent = |
|||
selectedAction && |
|||
actionTypes.find(t => t.name === selectedAction[eventTypeKey]).component |
|||
|
|||
const deleteAction = index => { |
|||
actions.splice(index, 1) |
|||
actions = actions |
|||
} |
|||
|
|||
const addAction = actionType => () => { |
|||
const newAction = { |
|||
parameters: {}, |
|||
[eventTypeKey]: actionType.name, |
|||
} |
|||
actions.push(newAction) |
|||
selectedAction = newAction |
|||
actions = actions |
|||
addActionDropdown.hide() |
|||
} |
|||
|
|||
const selectAction = action => () => { |
|||
selectedAction = action |
|||
} |
|||
|
|||
const saveEventData = async () => { |
|||
// e.g. The Trigger Automation action exposes beforeSave, so it can |
|||
// create any automations it needs to |
|||
for (let action of actions) { |
|||
if (action[eventTypeKey] === "Trigger Automation") { |
|||
await createAutomation(action.parameters) |
|||
} |
|||
} |
|||
dispatch("change", actions) |
|||
} |
|||
|
|||
// called by the parent modal when actions are saved |
|||
const createAutomation = async parameters => { |
|||
if (parameters.automationId || !parameters.newAutomationName) return |
|||
|
|||
await automationStore.actions.create({ name: parameters.newAutomationName }) |
|||
|
|||
const appActionDefinition = $automationStore.blockDefinitions.TRIGGER.APP |
|||
|
|||
const newBlock = $automationStore.selectedAutomation.constructBlock( |
|||
"TRIGGER", |
|||
"APP", |
|||
appActionDefinition |
|||
) |
|||
|
|||
newBlock.inputs = { |
|||
fields: Object.entries(parameters.fields).reduce( |
|||
(fields, [key, value]) => { |
|||
fields[key] = value.type |
|||
return fields |
|||
}, |
|||
{} |
|||
), |
|||
} |
|||
|
|||
automationStore.actions.addBlockToAutomation(newBlock) |
|||
|
|||
await automationStore.actions.save($automationStore.selectedAutomation) |
|||
|
|||
parameters.automationId = $automationStore.selectedAutomation.automation._id |
|||
delete parameters.newAutomationName |
|||
} |
|||
</script> |
|||
|
|||
<ModalContent title="Actions" confirmText="Save" onConfirm={saveEventData}> |
|||
<div slot="header"> |
|||
<div bind:this={addActionButton}> |
|||
<TextButton text small blue on:click={addActionDropdown.show}> |
|||
<div style="height: 20px; width: 20px;"> |
|||
<AddIcon /> |
|||
</div> |
|||
Add Action |
|||
</TextButton> |
|||
</div> |
|||
<DropdownMenu |
|||
bind:this={addActionDropdown} |
|||
anchor={addActionButton} |
|||
align="right"> |
|||
<div class="available-actions-container"> |
|||
{#each actionTypes as actionType} |
|||
<div class="available-action" on:click={addAction(actionType)}> |
|||
<span>{actionType.name}</span> |
|||
</div> |
|||
{/each} |
|||
</div> |
|||
</DropdownMenu> |
|||
</div> |
|||
|
|||
<div class="actions-container"> |
|||
{#if actions && actions.length > 0} |
|||
{#each actions as action, index} |
|||
<div class="action-container"> |
|||
<div class="action-header" on:click={selectAction(action)}> |
|||
<Body small lh>{index + 1}. {action[eventTypeKey]}</Body> |
|||
<div class="row-expander" class:rotate={action !== selectedAction}> |
|||
<ArrowDownIcon /> |
|||
</div> |
|||
</div> |
|||
{#if action === selectedAction} |
|||
<div class="selected-action-container"> |
|||
<svelte:component |
|||
this={selectedActionComponent} |
|||
parameters={selectedAction.parameters} /> |
|||
<div class="delete-action-button"> |
|||
<TextButton text medium on:click={() => deleteAction(index)}> |
|||
Delete |
|||
</TextButton> |
|||
</div> |
|||
</div> |
|||
{/if} |
|||
</div> |
|||
{/each} |
|||
{/if} |
|||
</div> |
|||
|
|||
<div slot="footer"> |
|||
<a href="https://docs.budibase.com">Learn more about Actions</a> |
|||
</div> |
|||
</ModalContent> |
|||
|
|||
<style> |
|||
.action-header { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: center; |
|||
} |
|||
|
|||
.action-header > p { |
|||
flex: 1; |
|||
} |
|||
|
|||
.row-expander { |
|||
height: 30px; |
|||
width: 30px; |
|||
} |
|||
|
|||
.available-action { |
|||
padding: var(--spacing-m); |
|||
font-size: var(--font-size-m); |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.available-action:hover { |
|||
background: var(--grey-2); |
|||
} |
|||
|
|||
.actions-container { |
|||
flex: 1; |
|||
min-height: 0; |
|||
padding-top: 0; |
|||
border: var(--border-light); |
|||
border-width: 0 0 1px 0; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.action-container { |
|||
border: var(--border-light); |
|||
border-width: 1px 0 0 0; |
|||
} |
|||
|
|||
.selected-action-container { |
|||
padding-bottom: var(--spacing-s); |
|||
padding-top: var(--spacing-s); |
|||
} |
|||
|
|||
.delete-action-button { |
|||
padding-top: var(--spacing-l); |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
flex-direction: row; |
|||
} |
|||
|
|||
a { |
|||
flex: 1; |
|||
color: var(--grey-5); |
|||
font-size: var(--font-size-s); |
|||
text-decoration: none; |
|||
} |
|||
a:hover { |
|||
color: var(--blue); |
|||
} |
|||
|
|||
.rotate :global(svg) { |
|||
transform: rotate(90deg); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,38 @@ |
|||
<script> |
|||
import { Select, Label } from "@budibase/bbui" |
|||
import { currentAsset, store } from "builderStore" |
|||
import { getActionProviderComponents } from "builderStore/dataBinding" |
|||
|
|||
export let parameters |
|||
|
|||
$: actionProviders = getActionProviderComponents( |
|||
$currentAsset, |
|||
$store.selectedComponentId, |
|||
"ValidateForm" |
|||
) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<Label small>Form</Label> |
|||
<Select thin secondary bind:value={parameters.componentId}> |
|||
<option value="" /> |
|||
{#if actionProviders} |
|||
{#each actionProviders as component} |
|||
<option value={component._id}>{component._instanceName}</option> |
|||
{/each} |
|||
{/if} |
|||
</Select> |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
display: flex; |
|||
flex-direction: row; |
|||
align-items: baseline; |
|||
} |
|||
|
|||
.root :global(> div) { |
|||
flex: 1; |
|||
margin-left: var(--spacing-l); |
|||
} |
|||
</style> |
|||
@ -0,0 +1,77 @@ |
|||
<script> |
|||
import { Button, Drawer, Spacer, Body } from "@budibase/bbui" |
|||
import { createEventDispatcher } from "svelte" |
|||
import { notifier } from "builderStore/store/notifications" |
|||
import { |
|||
getDatasourceForProvider, |
|||
getSchemaForDatasource, |
|||
} from "builderStore/dataBinding" |
|||
import SaveFields from "./EventsEditor/actions/SaveFields.svelte" |
|||
|
|||
const dispatch = createEventDispatcher() |
|||
|
|||
export let value = {} |
|||
export let componentInstance |
|||
let drawer |
|||
let tempValue = value |
|||
|
|||
$: schemaFields = getSchemaFields(componentInstance) |
|||
|
|||
const getSchemaFields = component => { |
|||
const datasource = getDatasourceForProvider(component) |
|||
const { schema } = getSchemaForDatasource(datasource) |
|||
return Object.values(schema || {}) |
|||
} |
|||
|
|||
const saveFilter = async () => { |
|||
dispatch("change", tempValue) |
|||
notifier.success("Filters saved.") |
|||
drawer.hide() |
|||
} |
|||
|
|||
const onFieldsChanged = event => { |
|||
tempValue = event.detail |
|||
} |
|||
</script> |
|||
|
|||
<Button secondary wide on:click={drawer.show}>Define Filters</Button> |
|||
<Drawer bind:this={drawer} title={'Filtering'}> |
|||
<heading slot="buttons"> |
|||
<Button thin blue on:click={saveFilter}>Save</Button> |
|||
</heading> |
|||
<div slot="body"> |
|||
<div class="root"> |
|||
<Body small grey> |
|||
{#if !Object.keys(tempValue || {}).length} |
|||
Add your first filter column. |
|||
{:else} |
|||
Results are filtered to only those which match all of the following |
|||
constaints. |
|||
{/if} |
|||
</Body> |
|||
<Spacer medium /> |
|||
<div class="fields"> |
|||
<SaveFields |
|||
parameterFields={value} |
|||
{schemaFields} |
|||
valueLabel="Equals" |
|||
on:change={onFieldsChanged} /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</Drawer> |
|||
|
|||
<style> |
|||
.root { |
|||
padding: var(--spacing-l); |
|||
min-height: calc(40vh - 2 * var(--spacing-l)); |
|||
} |
|||
|
|||
.fields { |
|||
display: grid; |
|||
column-gap: var(--spacing-l); |
|||
row-gap: var(--spacing-s); |
|||
grid-template-columns: auto 1fr auto 1fr auto; |
|||
align-items: baseline; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,59 @@ |
|||
<script> |
|||
import { DataList } from "@budibase/bbui" |
|||
import { |
|||
getDatasourceForProvider, |
|||
getSchemaForDatasource, |
|||
} from "builderStore/dataBinding" |
|||
import { currentAsset } from "builderStore" |
|||
import { findClosestMatchingComponent } from "builderStore/storeUtils" |
|||
|
|||
export let componentInstance |
|||
export let value |
|||
export let onChange |
|||
export let type |
|||
|
|||
$: form = findClosestMatchingComponent( |
|||
$currentAsset.props, |
|||
componentInstance._id, |
|||
component => component._component === "@budibase/standard-components/form" |
|||
) |
|||
$: datasource = getDatasourceForProvider(form) |
|||
$: schema = getSchemaForDatasource(datasource, true).schema |
|||
$: options = getOptions(schema, type) |
|||
|
|||
const getOptions = (schema, fieldType) => { |
|||
let entries = Object.entries(schema ?? {}) |
|||
if (fieldType) { |
|||
entries = entries.filter(entry => entry[1].type === fieldType) |
|||
} |
|||
return entries.map(entry => entry[0]) |
|||
} |
|||
|
|||
const handleBlur = () => onChange(value) |
|||
</script> |
|||
|
|||
<div> |
|||
<DataList |
|||
editable |
|||
secondary |
|||
extraThin |
|||
on:blur={handleBlur} |
|||
on:change |
|||
bind:value> |
|||
<option value="" /> |
|||
{#each options as option} |
|||
<option value={option}>{option}</option> |
|||
{/each} |
|||
</DataList> |
|||
</div> |
|||
|
|||
<style> |
|||
div { |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
div :global(> div) { |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="longform" /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FieldSelect from "./FieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FieldSelect {...$$props} multiselect /> |
|||
@ -1,5 +0,0 @@ |
|||
<script> |
|||
import TableViewFieldSelect from "./TableViewFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<TableViewFieldSelect {...$$props} multiselect /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="number" /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="options" /> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="link" /> |
|||
@ -0,0 +1,7 @@ |
|||
<script> |
|||
import DatasourceSelect from "./DatasourceSelect.svelte" |
|||
|
|||
const otherSources = [{ name: "Custom", label: "Custom" }] |
|||
</script> |
|||
|
|||
<DatasourceSelect on:change {...$$props} {otherSources} /> |
|||
@ -1,80 +0,0 @@ |
|||
<script> |
|||
import { DataList } from "@budibase/bbui" |
|||
import { createEventDispatcher } from "svelte" |
|||
import { store, allScreens, currentAsset } from "builderStore" |
|||
import { getBindableProperties } from "builderStore/dataBinding" |
|||
|
|||
export let value = "" |
|||
|
|||
$: urls = getUrls($allScreens, $currentAsset, $store.selectedComponentId) |
|||
|
|||
// Update value on blur |
|||
const dispatch = createEventDispatcher() |
|||
const handleBlur = () => dispatch("change", value) |
|||
|
|||
// Get all valid screen URL, as well as detail screens which can be used in |
|||
// the current data context |
|||
const getUrls = (screens, asset, componentId) => { |
|||
// Get all screens which aren't detail screens |
|||
let urls = screens |
|||
.filter(screen => !screen.props._component.endsWith("/rowdetail")) |
|||
.map(screen => ({ |
|||
name: screen.props._instanceName, |
|||
url: screen.routing.route, |
|||
sort: screen.props._component, |
|||
})) |
|||
|
|||
// Add detail screens enriched with the current data context |
|||
const bindableProperties = getBindableProperties(asset.props, componentId) |
|||
screens |
|||
.filter(screen => screen.props._component.endsWith("/rowdetail")) |
|||
.forEach(detailScreen => { |
|||
// Find any _id bindings that match the detail screen's table |
|||
const binding = bindableProperties.find(p => { |
|||
return ( |
|||
p.type === "context" && |
|||
p.runtimeBinding.endsWith("._id") && |
|||
p.tableId === detailScreen.props.table |
|||
) |
|||
}) |
|||
if (binding) { |
|||
urls.push({ |
|||
name: detailScreen.props._instanceName, |
|||
url: detailScreen.routing.route.replace( |
|||
":id", |
|||
`{{ ${binding.runtimeBinding} }}` |
|||
), |
|||
sort: detailScreen.props._component, |
|||
}) |
|||
} |
|||
}) |
|||
|
|||
return urls |
|||
} |
|||
</script> |
|||
|
|||
<div> |
|||
<DataList |
|||
editable |
|||
secondary |
|||
extraThin |
|||
on:blur={handleBlur} |
|||
on:change |
|||
bind:value> |
|||
<option value="" /> |
|||
{#each urls as url} |
|||
<option value={url.url}>{url.name}</option> |
|||
{/each} |
|||
</DataList> |
|||
</div> |
|||
|
|||
<style> |
|||
div { |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
div :global(> div) { |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,5 @@ |
|||
<script> |
|||
import FormFieldSelect from "./FormFieldSelect.svelte" |
|||
</script> |
|||
|
|||
<FormFieldSelect {...$$props} type="string" /> |
|||
@ -0,0 +1,50 @@ |
|||
<script> |
|||
import { Button, Input } from "@budibase/bbui" |
|||
|
|||
export let object = {} |
|||
export let readOnly |
|||
|
|||
let fields = Object.entries(object).map(([name, value]) => ({ name, value })) |
|||
|
|||
$: object = fields.reduce( |
|||
(acc, next) => ({ ...acc, [next.name]: next.value }), |
|||
{} |
|||
) |
|||
|
|||
function addEntry() { |
|||
fields = [...fields, {}] |
|||
} |
|||
|
|||
function deleteEntry(idx) { |
|||
fields.splice(idx, 1) |
|||
fields = fields |
|||
} |
|||
</script> |
|||
|
|||
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. --> |
|||
<div class="container" class:readOnly> |
|||
{#each fields as field, idx} |
|||
<Input placeholder="Key" thin outline bind:value={field.name} /> |
|||
<Input placeholder="Value" thin outline bind:value={field.value} /> |
|||
{#if !readOnly} |
|||
<i class="ri-close-circle-fill" on:click={() => deleteEntry(idx)} /> |
|||
{/if} |
|||
{/each} |
|||
</div> |
|||
{#if !readOnly} |
|||
<Button secondary thin outline on:click={addEntry}>Add</Button> |
|||
{/if} |
|||
|
|||
<style> |
|||
.container { |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr 20px; |
|||
grid-gap: var(--spacing-m); |
|||
align-items: center; |
|||
margin-bottom: var(--spacing-m); |
|||
} |
|||
|
|||
.ri-close-circle-fill { |
|||
cursor: pointer; |
|||
} |
|||
</style> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue