merge
This commit is contained in:
commit
78ae3f194c
|
@ -53,6 +53,7 @@ export const getStore = () => {
|
||||||
|
|
||||||
store.saveScreen = saveScreen(store)
|
store.saveScreen = saveScreen(store)
|
||||||
store.setCurrentScreen = setCurrentScreen(store)
|
store.setCurrentScreen = setCurrentScreen(store)
|
||||||
|
store.deleteScreens = deleteScreens(store)
|
||||||
store.setCurrentPage = setCurrentPage(store)
|
store.setCurrentPage = setCurrentPage(store)
|
||||||
store.createScreen = createScreen(store)
|
store.createScreen = createScreen(store)
|
||||||
store.addStylesheet = addStylesheet(store)
|
store.addStylesheet = addStylesheet(store)
|
||||||
|
@ -185,6 +186,26 @@ const setCurrentScreen = store => screenName => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deleteScreens = store => (screens, pageName = null) => {
|
||||||
|
if (!(screens instanceof Array)) {
|
||||||
|
screens = [screens]
|
||||||
|
}
|
||||||
|
store.update(state => {
|
||||||
|
if (pageName == null) {
|
||||||
|
pageName = state.pages.main.name
|
||||||
|
}
|
||||||
|
for (let screen of screens) {
|
||||||
|
state.screens = state.screens.filter(c => c.name !== screen.name)
|
||||||
|
// Remove screen from current page as well
|
||||||
|
state.pages[pageName]._screens = state.pages[pageName]._screens.filter(
|
||||||
|
scr => scr.name !== screen.name
|
||||||
|
)
|
||||||
|
api.delete(`/_builder/api/pages/${pageName}/screens/${screen.name}`)
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const savePage = store => async page => {
|
const savePage = store => async page => {
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (state.currentFrontEndType !== "page" || !state.currentPageName) {
|
if (state.currentFrontEndType !== "page" || !state.currentPageName) {
|
||||||
|
|
|
@ -98,7 +98,7 @@ const createScreen = table => ({
|
||||||
label: "Deals",
|
label: "Deals",
|
||||||
name: `all_${table._id}`,
|
name: `all_${table._id}`,
|
||||||
tableId: table._id,
|
tableId: table._id,
|
||||||
isTable: true,
|
type: "table",
|
||||||
},
|
},
|
||||||
_instanceName: `${table.name} Table`,
|
_instanceName: `${table.name} Table`,
|
||||||
_children: [],
|
_children: [],
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore, store } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import screenTemplates from "builderStore/store/screenTemplates"
|
||||||
|
import api from "builderStore/api"
|
||||||
|
|
||||||
export let table
|
export let table
|
||||||
|
|
||||||
|
@ -13,6 +15,8 @@
|
||||||
let confirmDeleteDialog
|
let confirmDeleteDialog
|
||||||
let error = ""
|
let error = ""
|
||||||
let originalName = table.name
|
let originalName = table.name
|
||||||
|
let templateScreens
|
||||||
|
let willBeDeleted
|
||||||
|
|
||||||
$: fields = Object.keys(table.schema)
|
$: fields = Object.keys(table.schema)
|
||||||
|
|
||||||
|
@ -26,12 +30,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function showModal() {
|
function showModal() {
|
||||||
|
const screens = $store.allScreens
|
||||||
|
templateScreens = screens.filter(screen => screen.props.table === table._id)
|
||||||
|
willBeDeleted = ["All table data"].concat(
|
||||||
|
templateScreens.map(screen => `Screen ${screen.props._instanceName}`)
|
||||||
|
)
|
||||||
hideEditor()
|
hideEditor()
|
||||||
confirmDeleteDialog.show()
|
confirmDeleteDialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteTable() {
|
async function deleteTable() {
|
||||||
await backendUiStore.actions.tables.delete(table)
|
await backendUiStore.actions.tables.delete(table)
|
||||||
|
store.deleteScreens(templateScreens)
|
||||||
notifier.success("Table deleted")
|
notifier.success("Table deleted")
|
||||||
hideEditor()
|
hideEditor()
|
||||||
}
|
}
|
||||||
|
@ -98,10 +108,21 @@
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
bind:this={confirmDeleteDialog}
|
bind:this={confirmDeleteDialog}
|
||||||
body={`Are you sure you wish to delete the table '${table.name}'? Your data will be deleted and this action cannot be undone.`}
|
|
||||||
okText="Delete Table"
|
okText="Delete Table"
|
||||||
onOk={deleteTable}
|
onOk={deleteTable}
|
||||||
title="Confirm Delete" />
|
title="Confirm Delete">
|
||||||
|
Are you sure you wish to delete the table
|
||||||
|
<i>{table.name}?</i>
|
||||||
|
The following will also be deleted:
|
||||||
|
<b>
|
||||||
|
<div class="delete-items">
|
||||||
|
{#each willBeDeleted as item}
|
||||||
|
<div>{item}</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</b>
|
||||||
|
This action cannot be undone.
|
||||||
|
</ConfirmDialog>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
div.icon {
|
div.icon {
|
||||||
|
@ -115,6 +136,17 @@
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.delete-items {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.delete-items div {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
padding: var(--spacing-xl);
|
padding: var(--spacing-xl);
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|
|
@ -12,13 +12,18 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<input type="checkbox" class="checkbox" id="_checkbox" />
|
<input type="checkbox" class="checkbox" id="_checkbox" />
|
||||||
<div on:click={handleChange}>
|
<div class="checkbox-container" on:click={handleChange}>
|
||||||
<div class="check-div" class:checked>
|
<div class="check-div" class:checked>
|
||||||
<div class="tick_mark" />
|
<div class="tick_mark" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.checkbox-container {
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@
|
||||||
|
|
||||||
<Modal bind:this={modal} on:hide={onCancel}>
|
<Modal bind:this={modal} on:hide={onCancel}>
|
||||||
<ModalContent onConfirm={onOk} {title} confirmText={okText} {cancelText} red>
|
<ModalContent onConfirm={onOk} {title} confirmText={okText} {cancelText} red>
|
||||||
<div class="body">{body}</div>
|
<div class="body">
|
||||||
|
{body}
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
_id: "screenslot-placeholder",
|
_id: "screenslot-placeholder",
|
||||||
_component: "@budibase/standard-components/container",
|
_component: "@budibase/standard-components/container",
|
||||||
_styles: {
|
_styles: {
|
||||||
normal: {},
|
normal: {
|
||||||
|
flex: "1 1 auto",
|
||||||
|
},
|
||||||
hover: {},
|
hover: {},
|
||||||
active: {},
|
active: {},
|
||||||
selected: {},
|
selected: {},
|
||||||
|
@ -46,6 +48,7 @@
|
||||||
display: "flex",
|
display: "flex",
|
||||||
"flex-direction": "column",
|
"flex-direction": "column",
|
||||||
"align-items": "center",
|
"align-items": "center",
|
||||||
|
flex: "1 1 auto",
|
||||||
},
|
},
|
||||||
hover: {},
|
hover: {},
|
||||||
active: {},
|
active: {},
|
||||||
|
|
|
@ -11,38 +11,22 @@ export default `<html>
|
||||||
*, *:before, *:after {
|
*, *:before, *:after {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.lay-__screenslot__text {
|
.container-screenslot-placeholder {
|
||||||
width: 100%;
|
display: flex;
|
||||||
height: 100%;
|
align-items: center;
|
||||||
display: flex;
|
justify-content: center;
|
||||||
justify-content: center;
|
padding: 20px;
|
||||||
align-items: center;
|
text-align: center;
|
||||||
padding: 20px 0;
|
border-style: dashed !important;
|
||||||
border: dashed 2px #ccc;
|
border-width: 1px;
|
||||||
font-family: sans-serif;
|
color: #000000;
|
||||||
font-size: 1.2rem;
|
background: #fafafa;
|
||||||
color: #999;
|
flex: 1 1 auto;
|
||||||
text-transform: uppercase;
|
}
|
||||||
font-weight: bold;
|
.container-screenslot-placeholder span {
|
||||||
}
|
display: block;
|
||||||
|
margin-bottom: 10px;
|
||||||
.container-screenslot-placeholder {
|
}
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
text-align: center;
|
|
||||||
border-style: dashed !important;
|
|
||||||
border-width: 1px;
|
|
||||||
color: #000000;
|
|
||||||
background: #fafafa;
|
|
||||||
height: 94%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-screenslot-placeholder span {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
<script src='/assets/budibase-client.js'></script>
|
<script src='/assets/budibase-client.js'></script>
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -17,26 +17,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteScreen = () => {
|
const deleteScreen = () => {
|
||||||
|
store.deleteScreens(screen, $store.currentPageName)
|
||||||
|
// update the page if required
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
// Remove screen from screens
|
|
||||||
const screens = state.screens.filter(c => c.name !== screen.name)
|
|
||||||
state.screens = screens
|
|
||||||
|
|
||||||
// Remove screen from current page as well
|
|
||||||
const pageScreens = state.pages[state.currentPageName]._screens.filter(
|
|
||||||
scr => scr.name !== screen.name
|
|
||||||
)
|
|
||||||
state.pages[state.currentPageName]._screens = pageScreens
|
|
||||||
|
|
||||||
if (state.currentPreviewItem.name === screen.name) {
|
if (state.currentPreviewItem.name === screen.name) {
|
||||||
store.setCurrentPage($store.currentPageName)
|
store.setCurrentPage($store.currentPageName)
|
||||||
$goto(`./:page/page-layout`)
|
$goto(`./:page/page-layout`)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.delete(
|
|
||||||
`/_builder/api/pages/${state.currentPageName}/screens/${screen.name}`
|
|
||||||
)
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,9 +61,9 @@
|
||||||
sort: detailScreen.props._component,
|
sort: detailScreen.props._component,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return urls
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return urls
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -261,6 +261,16 @@ export const padding = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export const size = [
|
export const size = [
|
||||||
|
{
|
||||||
|
label: "Flex",
|
||||||
|
key: "flex",
|
||||||
|
control: OptionSelect,
|
||||||
|
defaultValue: "0 1 auto",
|
||||||
|
options: [
|
||||||
|
{ label: "Shrink", value: "0 1 auto" },
|
||||||
|
{ label: "Grow", value: "1 1 auto" },
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Width",
|
label: "Width",
|
||||||
key: "width",
|
key: "width",
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import fetchBindableProperties from "../src/builderStore/fetchBindableProperties"
|
import fetchBindableProperties from "../src/builderStore/fetchBindableProperties"
|
||||||
|
|
||||||
describe("fetch bindable properties", () => {
|
describe("fetch bindable properties", () => {
|
||||||
|
|
||||||
it("should return bindable properties from screen components", () => {
|
it("should return bindable properties from screen components", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "heading-id",
|
componentInstanceId: "heading-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const componentBinding = result.find(r => r.instance._id === "search-input-id" && r.type === "instance")
|
const componentBinding = result.find(
|
||||||
|
r => r.instance._id === "search-input-id" && r.type === "instance"
|
||||||
|
)
|
||||||
expect(componentBinding).toBeDefined()
|
expect(componentBinding).toBeDefined()
|
||||||
expect(componentBinding.type).toBe("instance")
|
expect(componentBinding.type).toBe("instance")
|
||||||
expect(componentBinding.runtimeBinding).toBe("search-input-id.value")
|
expect(componentBinding.runtimeBinding).toBe("search-input-id.value")
|
||||||
|
@ -16,28 +17,38 @@ describe("fetch bindable properties", () => {
|
||||||
it("should not return bindable components when not in their context", () => {
|
it("should not return bindable components when not in their context", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "heading-id",
|
componentInstanceId: "heading-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const componentBinding = result.find(r => r.instance._id === "list-item-input-id")
|
const componentBinding = result.find(
|
||||||
|
r => r.instance._id === "list-item-input-id"
|
||||||
|
)
|
||||||
expect(componentBinding).not.toBeDefined()
|
expect(componentBinding).not.toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return table schema, when inside a context", () => {
|
it("should return table schema, when inside a context", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "list-item-input-id",
|
componentInstanceId: "list-item-input-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const contextBindings = result.filter(r => r.instance._id === "list-id" && r.type==="context")
|
const contextBindings = result.filter(
|
||||||
|
r => r.instance._id === "list-id" && r.type === "context"
|
||||||
|
)
|
||||||
// 2 fields + _id + _rev
|
// 2 fields + _id + _rev
|
||||||
expect(contextBindings.length).toBe(4)
|
expect(contextBindings.length).toBe(4)
|
||||||
|
|
||||||
const namebinding = contextBindings.find(b => b.runtimeBinding === "data.name")
|
const namebinding = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "data.name"
|
||||||
|
)
|
||||||
expect(namebinding).toBeDefined()
|
expect(namebinding).toBeDefined()
|
||||||
expect(namebinding.readableBinding).toBe("list-name.Test Table.name")
|
expect(namebinding.readableBinding).toBe("list-name.Test Table.name")
|
||||||
|
|
||||||
const descriptionbinding = contextBindings.find(b => b.runtimeBinding === "data.description")
|
const descriptionbinding = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "data.description"
|
||||||
|
)
|
||||||
expect(descriptionbinding).toBeDefined()
|
expect(descriptionbinding).toBeDefined()
|
||||||
expect(descriptionbinding.readableBinding).toBe("list-name.Test Table.description")
|
expect(descriptionbinding.readableBinding).toBe(
|
||||||
|
"list-name.Test Table.description"
|
||||||
|
)
|
||||||
|
|
||||||
const idbinding = contextBindings.find(b => b.runtimeBinding === "data._id")
|
const idbinding = contextBindings.find(b => b.runtimeBinding === "data._id")
|
||||||
expect(idbinding).toBeDefined()
|
expect(idbinding).toBeDefined()
|
||||||
|
@ -47,35 +58,51 @@ describe("fetch bindable properties", () => {
|
||||||
it("should return table schema, for grantparent context", () => {
|
it("should return table schema, for grantparent context", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "child-list-item-input-id",
|
componentInstanceId: "child-list-item-input-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const contextBindings = result.filter(r => r.type==="context")
|
const contextBindings = result.filter(r => r.type === "context")
|
||||||
// 2 fields + _id + _rev ... x 2 tables
|
// 2 fields + _id + _rev ... x 2 tables
|
||||||
expect(contextBindings.length).toBe(8)
|
expect(contextBindings.length).toBe(8)
|
||||||
|
|
||||||
const namebinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.name")
|
const namebinding_parent = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "parent.data.name"
|
||||||
|
)
|
||||||
expect(namebinding_parent).toBeDefined()
|
expect(namebinding_parent).toBeDefined()
|
||||||
expect(namebinding_parent.readableBinding).toBe("list-name.Test Table.name")
|
expect(namebinding_parent.readableBinding).toBe("list-name.Test Table.name")
|
||||||
|
|
||||||
const descriptionbinding_parent = contextBindings.find(b => b.runtimeBinding === "parent.data.description")
|
const descriptionbinding_parent = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "parent.data.description"
|
||||||
|
)
|
||||||
expect(descriptionbinding_parent).toBeDefined()
|
expect(descriptionbinding_parent).toBeDefined()
|
||||||
expect(descriptionbinding_parent.readableBinding).toBe("list-name.Test Table.description")
|
expect(descriptionbinding_parent.readableBinding).toBe(
|
||||||
|
"list-name.Test Table.description"
|
||||||
|
)
|
||||||
|
|
||||||
const namebinding_own = contextBindings.find(b => b.runtimeBinding === "data.name")
|
const namebinding_own = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "data.name"
|
||||||
|
)
|
||||||
expect(namebinding_own).toBeDefined()
|
expect(namebinding_own).toBeDefined()
|
||||||
expect(namebinding_own.readableBinding).toBe("child-list-name.Test Table.name")
|
expect(namebinding_own.readableBinding).toBe(
|
||||||
|
"child-list-name.Test Table.name"
|
||||||
|
)
|
||||||
|
|
||||||
const descriptionbinding_own = contextBindings.find(b => b.runtimeBinding === "data.description")
|
const descriptionbinding_own = contextBindings.find(
|
||||||
|
b => b.runtimeBinding === "data.description"
|
||||||
|
)
|
||||||
expect(descriptionbinding_own).toBeDefined()
|
expect(descriptionbinding_own).toBeDefined()
|
||||||
expect(descriptionbinding_own.readableBinding).toBe("child-list-name.Test Table.description")
|
expect(descriptionbinding_own.readableBinding).toBe(
|
||||||
|
"child-list-name.Test Table.description"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return bindable component props, from components in same context", () => {
|
it("should return bindable component props, from components in same context", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "list-item-heading-id",
|
componentInstanceId: "list-item-heading-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const componentBinding = result.find(r => r.instance._id === "list-item-input-id" && r.type === "instance")
|
const componentBinding = result.find(
|
||||||
|
r => r.instance._id === "list-item-input-id" && r.type === "instance"
|
||||||
|
)
|
||||||
expect(componentBinding).toBeDefined()
|
expect(componentBinding).toBeDefined()
|
||||||
expect(componentBinding.runtimeBinding).toBe("list-item-input-id.value")
|
expect(componentBinding.runtimeBinding).toBe("list-item-input-id.value")
|
||||||
})
|
})
|
||||||
|
@ -83,125 +110,140 @@ describe("fetch bindable properties", () => {
|
||||||
it("should not return components from child context", () => {
|
it("should not return components from child context", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "list-item-heading-id",
|
componentInstanceId: "list-item-heading-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
|
const componentBinding = result.find(
|
||||||
|
r =>
|
||||||
|
r.instance._id === "child-list-item-input-id" && r.type === "instance"
|
||||||
|
)
|
||||||
expect(componentBinding).not.toBeDefined()
|
expect(componentBinding).not.toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should return bindable component props, from components in same context (when nested context)", () => {
|
it("should return bindable component props, from components in same context (when nested context)", () => {
|
||||||
const result = fetchBindableProperties({
|
const result = fetchBindableProperties({
|
||||||
componentInstanceId: "child-list-item-heading-id",
|
componentInstanceId: "child-list-item-heading-id",
|
||||||
...testData()
|
...testData(),
|
||||||
})
|
})
|
||||||
const componentBinding = result.find(r => r.instance._id === "child-list-item-input-id" && r.type === "instance")
|
const componentBinding = result.find(
|
||||||
|
r =>
|
||||||
|
r.instance._id === "child-list-item-input-id" && r.type === "instance"
|
||||||
|
)
|
||||||
expect(componentBinding).toBeDefined()
|
expect(componentBinding).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const testData = () => {
|
const testData = () => {
|
||||||
|
|
||||||
const screen = {
|
const screen = {
|
||||||
instanceName: "test screen",
|
instanceName: "test screen",
|
||||||
name: "screen-id",
|
name: "screen-id",
|
||||||
route: "/",
|
route: "/",
|
||||||
props: {
|
props: {
|
||||||
_id:"screent-root-id",
|
_id: "screent-root-id",
|
||||||
_component: "@budibase/standard-components/container",
|
_component: "@budibase/standard-components/container",
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
_id: "heading-id",
|
_id: "heading-id",
|
||||||
_instanceName: "list item heading",
|
_instanceName: "list item heading",
|
||||||
_component: "@budibase/standard-components/heading",
|
_component: "@budibase/standard-components/heading",
|
||||||
text: "Screen Title"
|
text: "Screen Title",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: "search-input-id",
|
_id: "search-input-id",
|
||||||
_instanceName: "Search Input",
|
_instanceName: "Search Input",
|
||||||
_component: "@budibase/standard-components/input",
|
_component: "@budibase/standard-components/input",
|
||||||
value: "search phrase"
|
value: "search phrase",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: "list-id",
|
_id: "list-id",
|
||||||
_component: "@budibase/standard-components/list",
|
_component: "@budibase/standard-components/list",
|
||||||
_instanceName: "list-name",
|
_instanceName: "list-name",
|
||||||
table: { isTable: true, tableId: "test-table-id", label: "Test Table", name: "all_test-table-id" },
|
table: {
|
||||||
|
type: "table",
|
||||||
|
tableId: "test-table-id",
|
||||||
|
label: "Test Table",
|
||||||
|
name: "all_test-table-id",
|
||||||
|
},
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
_id: "list-item-heading-id",
|
_id: "list-item-heading-id",
|
||||||
_instanceName: "list item heading",
|
_instanceName: "list item heading",
|
||||||
_component: "@budibase/standard-components/heading",
|
_component: "@budibase/standard-components/heading",
|
||||||
text: "hello"
|
text: "hello",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: "list-item-input-id",
|
_id: "list-item-input-id",
|
||||||
_instanceName: "List Item Input",
|
_instanceName: "List Item Input",
|
||||||
_component: "@budibase/standard-components/input",
|
_component: "@budibase/standard-components/input",
|
||||||
value: "list item"
|
value: "list item",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: "child-list-id",
|
_id: "child-list-id",
|
||||||
_component: "@budibase/standard-components/list",
|
_component: "@budibase/standard-components/list",
|
||||||
_instanceName: "child-list-name",
|
_instanceName: "child-list-name",
|
||||||
table: { isTable: true, tableId: "test-table-id", label: "Test Table", name: "all_test-table-id"},
|
table: {
|
||||||
|
type: "table",
|
||||||
|
tableId: "test-table-id",
|
||||||
|
label: "Test Table",
|
||||||
|
name: "all_test-table-id",
|
||||||
|
},
|
||||||
_children: [
|
_children: [
|
||||||
{
|
{
|
||||||
_id: "child-list-item-heading-id",
|
_id: "child-list-item-heading-id",
|
||||||
_instanceName: "child list item heading",
|
_instanceName: "child list item heading",
|
||||||
_component: "@budibase/standard-components/heading",
|
_component: "@budibase/standard-components/heading",
|
||||||
text: "hello"
|
text: "hello",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
_id: "child-list-item-input-id",
|
_id: "child-list-item-input-id",
|
||||||
_instanceName: "Child List Item Input",
|
_instanceName: "Child List Item Input",
|
||||||
_component: "@budibase/standard-components/input",
|
_component: "@budibase/standard-components/input",
|
||||||
value: "child list item"
|
value: "child list item",
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const tables = [{
|
const tables = [
|
||||||
_id: "test-table-id",
|
{
|
||||||
name: "Test Table",
|
_id: "test-table-id",
|
||||||
|
name: "Test Table",
|
||||||
schema: {
|
schema: {
|
||||||
name: {
|
name: {
|
||||||
type: "string"
|
type: "string",
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: "string"
|
type: "string",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}]
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
"@budibase/standard-components/container" : {
|
"@budibase/standard-components/container": {
|
||||||
props: {},
|
props: {},
|
||||||
},
|
},
|
||||||
"@budibase/standard-components/list" : {
|
"@budibase/standard-components/list": {
|
||||||
context: "table",
|
context: "table",
|
||||||
props: {
|
props: {
|
||||||
table: "string"
|
table: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"@budibase/standard-components/input" : {
|
"@budibase/standard-components/input": {
|
||||||
bindable: "value",
|
bindable: "value",
|
||||||
props: {
|
props: {
|
||||||
value: "string"
|
value: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"@budibase/standard-components/heading" : {
|
"@budibase/standard-components/heading": {
|
||||||
props: {
|
props: {
|
||||||
text: "string"
|
text: "string",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return { screen, tables, components }
|
return { screen, tables, components }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ const ClientDb = require("../../db/clientDb")
|
||||||
const { getPackageForBuilder, buildPage } = require("../../utilities/builder")
|
const { getPackageForBuilder, buildPage } = require("../../utilities/builder")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const instanceController = require("./instance")
|
const instanceController = require("./instance")
|
||||||
const { copy, exists, readFile, writeFile } = require("fs-extra")
|
const { copy, existsSync, readFile, writeFile } = require("fs-extra")
|
||||||
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
||||||
const sqrl = require("squirrelly")
|
const sqrl = require("squirrelly")
|
||||||
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
const setBuilderToken = require("../../utilities/builder/setBuilderToken")
|
||||||
|
@ -116,6 +116,12 @@ exports.delete = async function(ctx) {
|
||||||
const db = new CouchDB(ClientDb.name(getClientId(ctx)))
|
const db = new CouchDB(ClientDb.name(getClientId(ctx)))
|
||||||
const app = await db.get(ctx.params.applicationId)
|
const app = await db.get(ctx.params.applicationId)
|
||||||
const result = await db.remove(app)
|
const result = await db.remove(app)
|
||||||
|
for (let instance of app.instances) {
|
||||||
|
const instanceDb = new CouchDB(instance._id)
|
||||||
|
await instanceDb.destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove top level directory
|
||||||
await fs.rmdir(join(budibaseAppsDir(), ctx.params.applicationId), {
|
await fs.rmdir(join(budibaseAppsDir(), ctx.params.applicationId), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
})
|
})
|
||||||
|
@ -137,7 +143,7 @@ const createEmptyAppPackage = async (ctx, app) => {
|
||||||
const appsFolder = budibaseAppsDir()
|
const appsFolder = budibaseAppsDir()
|
||||||
const newAppFolder = resolve(appsFolder, app._id)
|
const newAppFolder = resolve(appsFolder, app._id)
|
||||||
|
|
||||||
if (await exists(newAppFolder)) {
|
if (existsSync(newAppFolder)) {
|
||||||
ctx.throw(400, "App folder already exists for this application")
|
ctx.throw(400, "App folder already exists for this application")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* This controller is not currently fully implemented. Screens are
|
||||||
|
* currently managed as part of the pages API, please look in api/routes/page.js
|
||||||
|
* for routes and controllers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.fetch = async ctx => {
|
||||||
|
ctx.throw(501)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.save = async ctx => {
|
||||||
|
ctx.throw(501)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.destroy = async ctx => {
|
||||||
|
ctx.throw(501)
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
{
|
{
|
||||||
"title": "{{ name }}",
|
"title": "{{ name }}",
|
||||||
"favicon": "./_shared/favicon.png",
|
"favicon": "./_shared/favicon.png",
|
||||||
"stylesheets": [],
|
"stylesheets": [],
|
||||||
"componentLibraries": ["@budibase/standard-components"],
|
"componentLibraries": ["@budibase/standard-components"],
|
||||||
"props": {
|
"props": {
|
||||||
"_id": "private-master-root",
|
"_id": "private-master-root",
|
||||||
"_component": "@budibase/standard-components/container",
|
"_component": "@budibase/standard-components/container",
|
||||||
"_children": [
|
"_children": [
|
||||||
|
@ -62,10 +62,11 @@
|
||||||
"_component": "##builtin/screenslot",
|
"_component": "##builtin/screenslot",
|
||||||
"_styles": {
|
"_styles": {
|
||||||
"normal": {
|
"normal": {
|
||||||
"padding": "0px",
|
"flex": "1 1 auto",
|
||||||
"align-items": "flex-start",
|
"display": "flex",
|
||||||
"height": "100vh",
|
"flex-direction": "column",
|
||||||
"background-image": "None"
|
"justify-content": "flex-start",
|
||||||
|
"align-items": "stretch"
|
||||||
},
|
},
|
||||||
"hover": {},
|
"hover": {},
|
||||||
"active": {},
|
"active": {},
|
||||||
|
@ -84,10 +85,9 @@
|
||||||
"flex-direction": "column",
|
"flex-direction": "column",
|
||||||
"align-items": "stretch",
|
"align-items": "stretch",
|
||||||
"justify-content": "flex-start",
|
"justify-content": "flex-start",
|
||||||
"height": "",
|
|
||||||
"max-width": "",
|
|
||||||
"margin-right": "auto",
|
"margin-right": "auto",
|
||||||
"margin-left": "auto"
|
"margin-left": "auto",
|
||||||
|
"min-height": "100%"
|
||||||
},
|
},
|
||||||
"selected": {}
|
"selected": {}
|
||||||
},
|
},
|
||||||
|
@ -95,6 +95,6 @@
|
||||||
"className": "",
|
"className": "",
|
||||||
"onLoad": []
|
"onLoad": []
|
||||||
},
|
},
|
||||||
"_css": "",
|
"_css": "",
|
||||||
"uiFunctions": ""
|
"uiFunctions": ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { exists, readFile, writeFile, ensureDir } = require("fs-extra")
|
const { existsSync, readFile, writeFile, ensureDir } = require("fs-extra")
|
||||||
const { join, resolve } = require("./centralPath")
|
const { join, resolve } = require("./centralPath")
|
||||||
const Sqrl = require("squirrelly")
|
const Sqrl = require("squirrelly")
|
||||||
const uuid = require("uuid")
|
const uuid = require("uuid")
|
||||||
|
@ -28,7 +28,7 @@ const setCouchDbUrl = async opts => {
|
||||||
|
|
||||||
const createDevEnvFile = async opts => {
|
const createDevEnvFile = async opts => {
|
||||||
const destConfigFile = join(opts.dir, "./.env")
|
const destConfigFile = join(opts.dir, "./.env")
|
||||||
let createConfig = !(await exists(destConfigFile)) || opts.quiet
|
let createConfig = !existsSync(destConfigFile) || opts.quiet
|
||||||
if (createConfig) {
|
if (createConfig) {
|
||||||
const template = await readFile(
|
const template = await readFile(
|
||||||
resolve(__dirname, "..", "..", ".env.template"),
|
resolve(__dirname, "..", "..", ".env.template"),
|
||||||
|
|
|
@ -21,6 +21,11 @@
|
||||||
export let height = 500
|
export let height = 500
|
||||||
export let pagination
|
export let pagination
|
||||||
|
|
||||||
|
// These can never change at runtime so don't need to be reactive
|
||||||
|
let canEdit = editable && datasource && datasource.type !== "view"
|
||||||
|
let canAddDelete = editable && datasource && datasource.type === "table"
|
||||||
|
|
||||||
|
let store = _bb.store
|
||||||
let dataLoaded = false
|
let dataLoaded = false
|
||||||
let data
|
let data
|
||||||
let columnDefs
|
let columnDefs
|
||||||
|
@ -32,35 +37,42 @@
|
||||||
minWidth: 150,
|
minWidth: 150,
|
||||||
filter: true,
|
filter: true,
|
||||||
},
|
},
|
||||||
rowSelection: editable ? "multiple" : false,
|
rowSelection: canEdit ? "multiple" : false,
|
||||||
suppressRowClickSelection: !editable,
|
suppressRowClickSelection: !canEdit,
|
||||||
paginationAutoPageSize: true,
|
paginationAutoPageSize: true,
|
||||||
pagination,
|
pagination,
|
||||||
}
|
}
|
||||||
let store = _bb.store
|
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (datasource.tableId) {
|
if (!isEmpty(datasource)) {
|
||||||
const jsonTable = await _bb.api.get(`/api/tables/${datasource.tableId}`)
|
data = await fetchData(datasource, $store)
|
||||||
table = await jsonTable.json()
|
let schema = {}
|
||||||
const { schema } = table
|
|
||||||
if (!isEmpty(datasource)) {
|
// Get schema for datasource
|
||||||
data = await fetchData(datasource, $store)
|
// Views with "Calculate" applied provide their own schema.
|
||||||
columnDefs = Object.keys(schema).map((key, i) => {
|
// For everything else, use the tableId property to pull to table schema
|
||||||
return {
|
if (datasource.schema) {
|
||||||
headerCheckboxSelection: i === 0 && editable,
|
schema = datasource.schema
|
||||||
checkboxSelection: i === 0 && editable,
|
} else {
|
||||||
valueSetter: setters.get(schema[key].type),
|
const jsonTable = await _bb.api.get(`/api/tables/${datasource.tableId}`)
|
||||||
headerName: key.charAt(0).toUpperCase() + key.slice(1),
|
table = await jsonTable.json()
|
||||||
field: key,
|
schema = table.schema
|
||||||
hide: shouldHideField(key),
|
|
||||||
sortable: true,
|
|
||||||
editable: editable,
|
|
||||||
cellRenderer: getRenderer(schema[key], editable),
|
|
||||||
autoHeight: true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
columnDefs = Object.keys(schema).map((key, i) => {
|
||||||
|
return {
|
||||||
|
headerCheckboxSelection: i === 0 && canEdit,
|
||||||
|
checkboxSelection: i === 0 && canEdit,
|
||||||
|
valueSetter: setters.get(schema[key].type),
|
||||||
|
headerName: key.charAt(0).toUpperCase() + key.slice(1),
|
||||||
|
field: key,
|
||||||
|
hide: shouldHideField(key),
|
||||||
|
sortable: true,
|
||||||
|
editable: canEdit,
|
||||||
|
cellRenderer: getRenderer(schema[key], canEdit),
|
||||||
|
autoHeight: true,
|
||||||
|
}
|
||||||
|
})
|
||||||
dataLoaded = true
|
dataLoaded = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -117,7 +129,7 @@
|
||||||
|
|
||||||
<div style="--grid-height: {height}px">
|
<div style="--grid-height: {height}px">
|
||||||
{#if dataLoaded}
|
{#if dataLoaded}
|
||||||
{#if editable}
|
{#if canAddDelete}
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<CreateRowButton {_bb} {table} on:newRow={handleNewRow} />
|
<CreateRowButton {_bb} {table} on:newRow={handleNewRow} />
|
||||||
{#if selectedRows.length > 0}
|
{#if selectedRows.length > 0}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
export let color
|
export let color
|
||||||
export let stripeColor
|
export let stripeColor
|
||||||
export let borderColor
|
export let borderColor
|
||||||
export let datasource = {}
|
export let datasource
|
||||||
export let _bb
|
export let _bb
|
||||||
|
|
||||||
let data = []
|
let data = []
|
||||||
|
@ -35,14 +35,21 @@
|
||||||
const FETCH_TABLE_URL = `/api/tables/${tableId}`
|
const FETCH_TABLE_URL = `/api/tables/${tableId}`
|
||||||
const response = await _bb.api.get(FETCH_TABLE_URL)
|
const response = await _bb.api.get(FETCH_TABLE_URL)
|
||||||
const table = await response.json()
|
const table = await response.json()
|
||||||
schema = table.schema
|
return table.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!isEmpty(datasource)) {
|
if (!isEmpty(datasource)) {
|
||||||
data = await fetchData(datasource, $store)
|
data = await fetchData(datasource, $store)
|
||||||
if (data && data.length) {
|
|
||||||
await fetchTable(data[0].tableId)
|
// Get schema for datasource
|
||||||
|
// Views with "Calculate" applied provide their own schema.
|
||||||
|
// For everything else, use the tableId property to pull to table schema
|
||||||
|
if (datasource.schema) {
|
||||||
|
schema = datasource.schema
|
||||||
|
headers = Object.keys(schema).filter(shouldDisplayField)
|
||||||
|
} else {
|
||||||
|
schema = await fetchTable(datasource.tableId)
|
||||||
headers = Object.keys(schema).filter(shouldDisplayField)
|
headers = Object.keys(schema).filter(shouldDisplayField)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +61,6 @@
|
||||||
if (name === "type") return false
|
if (name === "type") return false
|
||||||
// tables are always tied to a single tableId, this is irrelevant
|
// tables are always tied to a single tableId, this is irrelevant
|
||||||
if (name === "tableId") return false
|
if (name === "tableId") return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,11 +101,11 @@
|
||||||
{#each sorted as row (row._id)}
|
{#each sorted as row (row._id)}
|
||||||
<tr>
|
<tr>
|
||||||
{#each headers as header}
|
{#each headers as header}
|
||||||
{#if schema[header]}
|
{#if schema[header] !== undefined}
|
||||||
<!-- Rudimentary solution for attachments on array given this entire table will be replaced by AG Grid -->
|
<!-- Rudimentary solution for attachments on array given this entire table will be replaced by AG Grid -->
|
||||||
{#if schema[header].type === 'attachment'}
|
{#if schema[header] && schema[header].type === 'attachment'}
|
||||||
<AttachmentList files={row[header]} />
|
<AttachmentList files={row[header]} />
|
||||||
{:else if schema[header].type === 'link'}
|
{:else if schema[header] && schema[header].type === 'link'}
|
||||||
<td>{row[header] ? row[header].length : 0} related row(s)</td>
|
<td>{row[header] ? row[header].length : 0} related row(s)</td>
|
||||||
{:else}
|
{:else}
|
||||||
<td>{row[header] == null ? '' : row[header]}</td>
|
<td>{row[header] == null ? '' : row[header]}</td>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
let rowId
|
let rowId
|
||||||
let errors = {}
|
let errors = {}
|
||||||
|
|
||||||
$: schema = $store.data && $store.data._table.schema
|
$: schema = $store.data && $store.data._table && $store.data._table.schema
|
||||||
$: fields = schema ? Object.keys(schema) : []
|
$: fields = schema ? Object.keys(schema) : []
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (table) {
|
if (table && typeof table === "string") {
|
||||||
const tableObj = await fetchTable(table)
|
const tableObj = await fetchTable(table)
|
||||||
row.tableId = table
|
row.tableId = table
|
||||||
row._table = tableObj
|
row._table = tableObj
|
||||||
|
|
|
@ -6,16 +6,16 @@ export default async function fetchData(datasource, store) {
|
||||||
if (name) {
|
if (name) {
|
||||||
let rows = []
|
let rows = []
|
||||||
if (type === "table") {
|
if (type === "table") {
|
||||||
rows = await fetchTableData()
|
rows = fetchTableData()
|
||||||
} else if (type === "view") {
|
} else if (type === "view") {
|
||||||
rows = await fetchViewData()
|
rows = fetchViewData()
|
||||||
} else if (type === "link") {
|
} else if (type === "link") {
|
||||||
rows = await fetchLinkedRowsData()
|
rows = fetchLinkedRowsData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch table schema so we can check for linked rows
|
// Fetch table schema so we can check for linked rows
|
||||||
if (rows && rows.length) {
|
if (rows && rows.length) {
|
||||||
const table = await fetchTable(rows[0].tableId)
|
const table = await fetchTable()
|
||||||
const keys = Object.keys(table.schema)
|
const keys = Object.keys(table.schema)
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
for (let key of keys) {
|
for (let key of keys) {
|
||||||
|
@ -27,10 +27,12 @@ export default async function fetchData(datasource, store) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchTable(id) {
|
async function fetchTable() {
|
||||||
const FETCH_TABLE_URL = `/api/tables/${id}`
|
const FETCH_TABLE_URL = `/api/tables/${datasource.tableId}`
|
||||||
const response = await api.get(FETCH_TABLE_URL)
|
const response = await api.get(FETCH_TABLE_URL)
|
||||||
return await response.json()
|
return await response.json()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue