diff --git a/lerna.json b/lerna.json
index bd38e87505..b3e7fe4677 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "0.2.1",
+ "version": "0.2.2",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 4ddf917bee..d9a4f56ef3 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "AGPL-3.0",
"private": true,
"scripts": {
@@ -63,8 +63,8 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.41.0",
- "@budibase/client": "^0.2.1",
+ "@budibase/bbui": "^1.44.0",
+ "@budibase/client": "^0.2.2",
"@budibase/colorpicker": "^1.0.1",
"@fortawesome/fontawesome-free": "^5.14.0",
"@sentry/browser": "5.19.1",
diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js
index f48b561ef0..3cdabf1ed5 100644
--- a/packages/builder/src/builderStore/store/backend.js
+++ b/packages/builder/src/builderStore/store/backend.js
@@ -1,4 +1,4 @@
-import { writable } from "svelte/store"
+import { writable, get } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import api from "../api"
@@ -62,16 +62,30 @@ export const getBackendUiStore = () => {
}),
save: async table => {
const updatedTable = cloneDeep(table)
+ const oldTable = get(store).tables.filter(t => t._id === table._id)[0]
+ const fieldNames = []
// update any renamed schema keys to reflect their names
- for (let key in updatedTable.schema) {
+ for (let key of Object.keys(updatedTable.schema)) {
+ // if field name has been seen before remove it
+ if (fieldNames.indexOf(key.toLowerCase()) !== -1) {
+ delete updatedTable.schema[key]
+ continue
+ }
const field = updatedTable.schema[key]
+ const oldField = oldTable?.schema[key]
+ // if the type has changed then revert back to the old field
+ if (oldField != null && oldField.type !== field.type) {
+ updatedTable.schema[key] = oldField
+ }
// field has been renamed
if (field.name && field.name !== key) {
updatedTable.schema[field.name] = field
updatedTable._rename = { old: key, updated: field.name }
delete updatedTable.schema[key]
}
+ // finally record this field has been used
+ fieldNames.push(key.toLowerCase())
}
const SAVE_TABLE_URL = `/api/tables`
diff --git a/packages/builder/src/builderStore/store/index.js b/packages/builder/src/builderStore/store/index.js
index 277f57584e..311d7a63a3 100644
--- a/packages/builder/src/builderStore/store/index.js
+++ b/packages/builder/src/builderStore/store/index.js
@@ -1,4 +1,4 @@
-import { values, cloneDeep } from "lodash/fp"
+import { cloneDeep } from "lodash/fp"
import getNewComponentName from "../getNewComponentName"
import { backendUiStore } from "builderStore"
import { writable, get } from "svelte/store"
@@ -129,7 +129,10 @@ const setPackage = (store, initial) => async pkg => {
initial.appId = pkg.application._id
initial.pages = pkg.pages
initial.hasAppPackage = true
- initial.screens = values(pkg.screens)
+ initial.screens = [
+ ...Object.values(main_screens),
+ ...Object.values(unauth_screens),
+ ]
initial.builtins = [getBuiltin("##builtin/screenslot")]
initial.appInstances = pkg.application.instances
initial.appId = pkg.application._id
diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
index 571007c092..50e90cddcf 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
@@ -1,20 +1,22 @@
+import sanitizeUrl from "./sanitizeUrl"
+import { rowListUrl } from "./rowListScreen"
+
export default function(tables) {
return tables.map(table => {
- const fields = Object.keys(table.schema)
- const heading = fields.length > 0 ? `{{ data.${fields[0]} }}` : "Add Row"
return {
name: `${table.name} - New`,
- create: () => createScreen(table, heading),
+ create: () => createScreen(table),
id: NEW_ROW_TEMPLATE,
}
})
}
+export const newRowUrl = table => sanitizeUrl(`/${table.name}/new`)
export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE"
-const createScreen = (table, heading) => ({
+const createScreen = table => ({
props: {
- _id: "",
+ _id: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
_component: "@budibase/standard-components/newrow",
_styles: {
normal: {},
@@ -25,43 +27,22 @@ const createScreen = (table, heading) => ({
table: table._id,
_children: [
{
- _id: "",
- _component: "@budibase/standard-components/heading",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- className: "",
- text: heading,
- type: "h1",
- _instanceName: "Heading 1",
- _children: [],
- },
- {
- _id: "",
- _component: "@budibase/standard-components/dataform",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- _instanceName: `${table.name} Form`,
- _children: [],
- },
- {
- _id: "",
+ _id: "ccad6cc135c7947a7ba9c631f655d6e0f",
_component: "@budibase/standard-components/container",
_styles: {
normal: {
- display: "flex",
- "flex-direction": "row",
- "align-items": "center",
- "justify-content": "flex-end",
+ width: "700px",
+ padding: "0px",
+ background: "white",
+ "border-radius": "0.5rem",
+ "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
+ margin: "auto",
+ "margin-top": "20px",
+ "padding-top": "48px",
+ "padding-bottom": "48px",
+ "padding-right": "48px",
+ "padding-left": "48px",
+ "margin-bottom": "20px",
},
hover: {},
active: {},
@@ -71,37 +52,187 @@ const createScreen = (table, heading) => ({
className: "",
onLoad: [],
type: "div",
- _instanceName: "Buttons Container",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Container",
_children: [
{
- _id: "",
- _component: "@budibase/standard-components/button",
+ _id: "c6e91622ba7984f468f70bf4bf5120246",
+ _component: "@budibase/standard-components/container",
_styles: {
normal: {
- "margin-right": "20px",
+ "font-size": "14px",
+ color: "#757575",
},
hover: {},
active: {},
selected: {},
},
_code: "",
- text: "Back",
className: "",
- disabled: false,
- onClick: [
+ onLoad: [],
+ type: "div",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Breadcrumbs",
+ _children: [
{
- parameters: {
- url: `/${table.name.toLowerCase()}`,
+ _id: "caa33353c252c4931b2a51b48a559a7fc",
+ _component: "@budibase/standard-components/link",
+ _styles: {
+ normal: {
+ color: "#757575",
+ "text-transform": "capitalize",
+ },
+ hover: {
+ color: "#4285f4",
+ },
+ active: {},
+ selected: {},
},
- "##eventHandlerType": "Navigate To",
+ _code: "",
+ url: `/${table.name.toLowerCase()}`,
+ openInNewTab: false,
+ text: table.name,
+ color: "",
+ hoverColor: "",
+ underline: false,
+ fontSize: "",
+ fontFamily: "initial",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Back Link",
+ _children: [],
+ },
+ {
+ _id: "c6e218170201040e7a74e2c8304fe1860",
+ _component: "@budibase/standard-components/text",
+ _styles: {
+ normal: {
+ "margin-right": "4px",
+ "margin-left": "4px",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: ">",
+ type: "none",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Arrow",
+ _children: [],
+ },
+ {
+ _id: "c799da1fa3a84442e947cc9199518f64c",
+ _component: "@budibase/standard-components/text",
+ _styles: {
+ normal: {
+ color: "#000000",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: "New",
+ type: "none",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Identifier",
+ _children: [],
},
],
- _instanceName: "Back Button",
- _children: [],
},
{
- _id: "",
- _component: "@budibase/standard-components/button",
+ _id: "cbd1637cd1e274287a3c28ef0bf235d08",
+ _component: "@budibase/standard-components/container",
+ _styles: {
+ normal: {
+ display: "flex",
+ "flex-direction": "row",
+ "justify-content": "space-between",
+ "align-items": "center",
+ "margin-top": "32px",
+ "margin-bottom": "32px",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ className: "",
+ onLoad: [],
+ type: "div",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Title Container",
+ _children: [
+ {
+ _id: "c98d3675d04114558bbf28661c5ccfb8e",
+ _component: "@budibase/standard-components/heading",
+ _styles: {
+ normal: {
+ margin: "0px",
+ "margin-bottom": "0px",
+ "margin-right": "0px",
+ "margin-top": "0px",
+ "margin-left": "0px",
+ flex: "1 1 auto",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ className: "",
+ text: "New Row",
+ type: "h3",
+ _instanceName: "Title",
+ _children: [],
+ },
+ {
+ _id: "cae402bd3c6a44618a8341bf7ab9ab086",
+ _component: "@budibase/standard-components/button",
+ _styles: {
+ normal: {
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ "margin-left": "16px",
+ },
+ hover: {
+ background: "#4285f4",
+ },
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: "Save",
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ contextPath: "data",
+ tableId: table._id,
+ },
+ "##eventHandlerType": "Save Row",
+ },
+ {
+ parameters: {
+ url: rowListUrl(table),
+ },
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ _instanceName: "Save Button",
+ _children: [],
+ },
+ ],
+ },
+ {
+ _id: "c5e6c98d7363640f9ad3a7d19c8c10f67",
+ _component: "@budibase/standard-components/dataformwide",
_styles: {
normal: {},
hover: {},
@@ -109,19 +240,8 @@ const createScreen = (table, heading) => ({
selected: {},
},
_code: "",
- text: "Save",
- className: "",
- disabled: false,
- onClick: [
- {
- parameters: {
- contextPath: "data",
- tableId: table._id,
- },
- "##eventHandlerType": "Save Row",
- },
- ],
- _instanceName: "Save Button",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Form",
_children: [],
},
],
@@ -130,6 +250,6 @@ const createScreen = (table, heading) => ({
_instanceName: `${table.name} - New`,
_code: "",
},
- route: `/${table.name.toLowerCase()}/new`,
+ route: newRowUrl(table),
name: "",
})
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
index 58ca2f6d05..a4f55f2fd1 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
@@ -1,7 +1,11 @@
+import sanitizeUrl from "./sanitizeUrl"
+import { rowListUrl } from "./rowListScreen"
+
export default function(tables) {
return tables.map(table => {
- const fields = Object.keys(table.schema)
- const heading = fields.length > 0 ? `{{ data.${fields[0]} }}` : "Detail"
+ const heading = table.primaryDisplay
+ ? `{{ data.${table.primaryDisplay} }}`
+ : null
return {
name: `${table.name} - Detail`,
create: () => createScreen(table, heading),
@@ -11,10 +15,11 @@ export default function(tables) {
}
export const ROW_DETAIL_TEMPLATE = "ROW_DETAIL_TEMPLATE"
+export const rowDetailUrl = table => sanitizeUrl(`/${table.name}/:id`)
const createScreen = (table, heading) => ({
props: {
- _id: "",
+ _id: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
_component: "@budibase/standard-components/rowdetail",
_styles: {
normal: {},
@@ -25,43 +30,22 @@ const createScreen = (table, heading) => ({
table: table._id,
_children: [
{
- _id: "",
- _component: "@budibase/standard-components/heading",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- className: "",
- text: heading,
- type: "h1",
- _instanceName: "Heading 1",
- _children: [],
- },
- {
- _id: "",
- _component: "@budibase/standard-components/dataform",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- _instanceName: `${table.name} Form`,
- _children: [],
- },
- {
- _id: "",
+ _id: "ccad6cc135c7947a7ba9c631f655d6e0f",
_component: "@budibase/standard-components/container",
_styles: {
normal: {
- display: "flex",
- "flex-direction": "row",
- "align-items": "center",
- "justify-content": "flex-end",
+ width: "700px",
+ padding: "0px",
+ background: "white",
+ "border-radius": "0.5rem",
+ "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
+ margin: "auto",
+ "margin-top": "20px",
+ "padding-top": "48px",
+ "padding-bottom": "48px",
+ "padding-right": "48px",
+ "padding-left": "48px",
+ "margin-bottom": "20px",
},
hover: {},
active: {},
@@ -71,37 +55,233 @@ const createScreen = (table, heading) => ({
className: "",
onLoad: [],
type: "div",
- _instanceName: "Buttons Container",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Container",
_children: [
{
- _id: "",
- _component: "@budibase/standard-components/button",
+ _id: "c6e91622ba7984f468f70bf4bf5120246",
+ _component: "@budibase/standard-components/container",
_styles: {
normal: {
- "margin-right": "20px",
+ "font-size": "14px",
+ color: "#757575",
},
hover: {},
active: {},
selected: {},
},
_code: "",
- text: "Back",
className: "",
- disabled: false,
- onClick: [
+ onLoad: [],
+ type: "div",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Breadcrumbs",
+ _children: [
{
- parameters: {
- url: `/${table.name.toLowerCase()}`,
+ _id: "caa33353c252c4931b2a51b48a559a7fc",
+ _component: "@budibase/standard-components/link",
+ _styles: {
+ normal: {
+ color: "#757575",
+ "text-transform": "capitalize",
+ },
+ hover: {
+ color: "#4285f4",
+ },
+ active: {},
+ selected: {},
},
- "##eventHandlerType": "Navigate To",
+ _code: "",
+ url: `/${table.name.toLowerCase()}`,
+ openInNewTab: false,
+ text: table.name,
+ color: "",
+ hoverColor: "",
+ underline: false,
+ fontSize: "",
+ fontFamily: "initial",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Back Link",
+ _children: [],
+ },
+ {
+ _id: "c6e218170201040e7a74e2c8304fe1860",
+ _component: "@budibase/standard-components/text",
+ _styles: {
+ normal: {
+ "margin-right": "4px",
+ "margin-left": "4px",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: ">",
+ type: "none",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Arrow",
+ _children: [],
+ },
+ {
+ _id: "c799da1fa3a84442e947cc9199518f64c",
+ _component: "@budibase/standard-components/text",
+ _styles: {
+ normal: {
+ color: "#000000",
+ "text-transform": "capitalize",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: heading || "Edit",
+ type: "none",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Identifier",
+ _children: [],
},
],
- _instanceName: "Back Button",
- _children: [],
},
{
- _id: "",
- _component: "@budibase/standard-components/button",
+ _id: "cbd1637cd1e274287a3c28ef0bf235d08",
+ _component: "@budibase/standard-components/container",
+ _styles: {
+ normal: {
+ display: "flex",
+ "flex-direction": "row",
+ "justify-content": "space-between",
+ "align-items": "center",
+ "margin-top": "32px",
+ "margin-bottom": "32px",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ className: "",
+ onLoad: [],
+ type: "div",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Title Container",
+ _children: [
+ {
+ _id: "c98d3675d04114558bbf28661c5ccfb8e",
+ _component: "@budibase/standard-components/heading",
+ _styles: {
+ normal: {
+ margin: "0px",
+ "margin-bottom": "0px",
+ "margin-right": "0px",
+ "margin-top": "0px",
+ "margin-left": "0px",
+ flex: "1 1 auto",
+ "text-transform": "capitalize",
+ },
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ className: "",
+ text: heading || "Edit Row",
+ type: "h3",
+ _instanceName: "Title",
+ _children: [],
+ },
+ {
+ _id: "c0a162cfb7d1c4bcfa8d24c290ccd1fd6",
+ _component: "@budibase/standard-components/button",
+ _styles: {
+ normal: {
+ background: "transparent",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#9e9e9e",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ "margin-right": "8px",
+ "margin-left": "16px",
+ },
+ hover: {
+ background: "transparent",
+ color: "#4285f4",
+ },
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: "Delete",
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ rowId: "{{ data._id }}",
+ revId: "{{ data._rev }}",
+ tableId: table._id,
+ },
+ "##eventHandlerType": "Delete Row",
+ },
+ {
+ parameters: {
+ url: rowListUrl(table),
+ },
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ _instanceName: "Delete Button",
+ _children: [],
+ },
+ {
+ _id: "cae402bd3c6a44618a8341bf7ab9ab086",
+ _component: "@budibase/standard-components/button",
+ _styles: {
+ normal: {
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ },
+ hover: {
+ background: "#4285f4",
+ },
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: "Save",
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ contextPath: "data",
+ tableId: table._id,
+ },
+ "##eventHandlerType": "Save Row",
+ },
+ {
+ parameters: {
+ url: rowListUrl(table),
+ },
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ _instanceName: "Save Button",
+ _children: [],
+ },
+ ],
+ },
+ {
+ _id: "c5e6c98d7363640f9ad3a7d19c8c10f67",
+ _component: "@budibase/standard-components/dataformwide",
_styles: {
normal: {},
hover: {},
@@ -109,19 +289,8 @@ const createScreen = (table, heading) => ({
selected: {},
},
_code: "",
- text: "Save",
- className: "",
- disabled: false,
- onClick: [
- {
- parameters: {
- contextPath: "data",
- tableId: table._id,
- },
- "##eventHandlerType": "Save Row",
- },
- ],
- _instanceName: "Save Button",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Form",
_children: [],
},
],
@@ -130,6 +299,6 @@ const createScreen = (table, heading) => ({
_instanceName: `${table.name} - Detail`,
_code: "",
},
- route: `/${table.name.toLowerCase()}/:id`,
+ route: rowDetailUrl(table),
name: "",
})
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
index c01fc9e3ae..5c71a45f1f 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
@@ -1,3 +1,6 @@
+import sanitizeUrl from "./sanitizeUrl"
+import { newRowUrl } from "./newRowScreen"
+
export default function(tables) {
return tables.map(table => {
return {
@@ -9,10 +12,11 @@ export default function(tables) {
}
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
+export const rowListUrl = table => sanitizeUrl(`/${table.name}`)
const createScreen = table => ({
props: {
- _id: "",
+ _id: "c7365379815e4457dbe703a886c2da43b",
_component: "@budibase/standard-components/container",
_styles: {
normal: {},
@@ -23,14 +27,23 @@ const createScreen = table => ({
type: "div",
_children: [
{
- _id: "",
+ _id: "cf51241fc063d4d87be032dd509fe0244",
_component: "@budibase/standard-components/container",
_styles: {
normal: {
- display: "flex",
- "flex-direction": "row",
- "justify-content": "space-between",
- "align-items": "center",
+ background: "white",
+ "border-radius": "0.5rem",
+ "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
+ margin: "auto",
+ "margin-top": "20px",
+ "border-width": "2px",
+ "border-color": "rgba(0, 0, 0, 0.1)",
+ "border-style": "None",
+ "padding-top": "48px",
+ "padding-bottom": "48px",
+ "padding-right": "48px",
+ "padding-left": "48px",
+ "margin-bottom": "20px",
},
hover: {},
active: {},
@@ -40,75 +53,120 @@ const createScreen = table => ({
className: "",
onLoad: [],
type: "div",
- _instanceName: "Header",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Container",
_children: [
{
- _id: "",
- _component: "@budibase/standard-components/heading",
+ _id: "c73294c301fd145aabe9bbbbd96a150ac",
+ _component: "@budibase/standard-components/container",
_styles: {
- normal: {},
+ normal: {
+ display: "flex",
+ "flex-direction": "row",
+ "justify-content": "space-between",
+ "align-items": "center",
+ "margin-bottom": "32px",
+ },
hover: {},
active: {},
selected: {},
},
_code: "",
className: "",
- text: `${table.name} List`,
- type: "h1",
- _instanceName: "Heading 1",
- _children: [],
- },
- {
- _id: "",
- _component: "@budibase/standard-components/button",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- text: "Create New",
- className: "",
- disabled: false,
- onClick: [
+ onLoad: [],
+ type: "div",
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Title Container",
+ _children: [
{
- parameters: {
- url: `/${table.name}/new`,
+ _id: "c2b77901df95a4d1ca7204c58300bc94b",
+ _component: "@budibase/standard-components/heading",
+ _styles: {
+ normal: {
+ margin: "0px",
+ flex: "1 1 auto",
+ "text-transform": "capitalize",
+ },
+ hover: {},
+ active: {},
+ selected: {},
},
- "##eventHandlerType": "Navigate To",
+ _code: "",
+ className: "",
+ text: table.name,
+ type: "h3",
+ _instanceName: "Title",
+ _children: [],
+ },
+ {
+ _id: "c12a82d77baf24ca9922ea0af7cd4f723",
+ _component: "@budibase/standard-components/button",
+ _styles: {
+ normal: {
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ },
+ hover: {
+ background: "#4285f4",
+ },
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ text: "Create New",
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ url: newRowUrl(table),
+ },
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ _instanceName: "New Button",
+ _children: [],
},
],
- _instanceName: "Create New Button",
+ },
+ {
+ _id: "ca686a2ed89c943e6bafb63fa66a3ead3",
+ _component: "@budibase/standard-components/datagrid",
+ _styles: {
+ normal: {},
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ datasource: {
+ label: table.name,
+ name: `all_${table._id}`,
+ tableId: table._id,
+ type: "table",
+ },
+ editable: false,
+ theme: "alpine",
+ height: "540",
+ pagination: true,
+ _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
+ _instanceName: "Grid",
_children: [],
+ detailUrl: `${table.name.toLowerCase()}/:id`,
},
],
},
- {
- _id: "",
- _component: "@budibase/standard-components/datagrid",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- datasource: {
- label: "Deals",
- name: `all_${table._id}`,
- tableId: table._id,
- type: "table",
- },
- _instanceName: `${table.name} Table`,
- _children: [],
- },
],
_instanceName: `${table.name} - List`,
_code: "",
className: "",
onLoad: [],
},
- route: `/${table.name.toLowerCase()}`,
+ route: rowListUrl(table),
name: "",
})
diff --git a/packages/builder/src/builderStore/store/screenTemplates/sanitizeUrl.js b/packages/builder/src/builderStore/store/screenTemplates/sanitizeUrl.js
new file mode 100644
index 0000000000..96b7633e41
--- /dev/null
+++ b/packages/builder/src/builderStore/store/screenTemplates/sanitizeUrl.js
@@ -0,0 +1,11 @@
+export default function(url) {
+ return url
+ .split("/")
+ .map(part => {
+ // if parameter, then use as is
+ if (part.startsWith(":")) return part
+ return encodeURIComponent(part.replace(/ /g, "-"))
+ })
+ .join("/")
+ .toLowerCase()
+}
diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte
index 4c0dc06d39..8354b464c0 100644
--- a/packages/builder/src/components/backend/DataTable/Table.svelte
+++ b/packages/builder/src/components/backend/DataTable/Table.svelte
@@ -132,7 +132,6 @@
font-size: 24px;
font-weight: 600;
text-rendering: optimizeLegibility;
- text-transform: capitalize;
margin-top: 0;
display: flex;
flex-direction: row;
diff --git a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
index 5b56e3d125..2ace2bb338 100644
--- a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte
@@ -1,5 +1,6 @@
@@ -88,6 +96,7 @@
{/if}
@@ -95,6 +104,7 @@
{#if field.type !== 'link'}
{/if}
diff --git a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte
index 2b74e9c288..25f74671fd 100644
--- a/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte
+++ b/packages/builder/src/components/backend/TableNavigator/popovers/EditTablePopover.svelte
@@ -42,6 +42,7 @@
async function deleteTable() {
await backendUiStore.actions.tables.delete(table)
store.deleteScreens(templateScreens)
+ await backendUiStore.actions.tables.fetch()
notifier.success("Table deleted")
hideEditor()
}
diff --git a/packages/builder/src/components/userInterface/ComponentsHierarchy.svelte b/packages/builder/src/components/userInterface/ComponentsHierarchy.svelte
index 8393299b12..add2a99d6a 100644
--- a/packages/builder/src/components/userInterface/ComponentsHierarchy.svelte
+++ b/packages/builder/src/components/userInterface/ComponentsHierarchy.svelte
@@ -27,15 +27,12 @@
const joinPath = join("/")
const normalizedName = name =>
- pipe(
- name,
- [
- trimCharsStart("./"),
- trimCharsStart("~/"),
- trimCharsStart("../"),
- trimChars(" "),
- ]
- )
+ pipe(name, [
+ trimCharsStart("./"),
+ trimCharsStart("~/"),
+ trimCharsStart("../"),
+ trimChars(" "),
+ ])
const changeScreen = screen => {
store.setCurrentScreen(screen.props._instanceName)
diff --git a/packages/builder/src/components/userInterface/DetailScreenSelect.svelte b/packages/builder/src/components/userInterface/DetailScreenSelect.svelte
new file mode 100644
index 0000000000..1e2908ab54
--- /dev/null
+++ b/packages/builder/src/components/userInterface/DetailScreenSelect.svelte
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/DeleteRow.svelte b/packages/builder/src/components/userInterface/EventsEditor/actions/DeleteRow.svelte
new file mode 100644
index 0000000000..6e6f6b6b21
--- /dev/null
+++ b/packages/builder/src/components/userInterface/EventsEditor/actions/DeleteRow.svelte
@@ -0,0 +1,91 @@
+
+
+
+ {#if idFields.length === 0}
+
+ Delete row can only be used within a component that provides data, such as
+ a List
+
+ {:else}
+
+
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/index.js b/packages/builder/src/components/userInterface/EventsEditor/actions/index.js
index 109a3104f8..1c07356ee9 100644
--- a/packages/builder/src/components/userInterface/EventsEditor/actions/index.js
+++ b/packages/builder/src/components/userInterface/EventsEditor/actions/index.js
@@ -1,5 +1,6 @@
import NavigateTo from "./NavigateTo.svelte"
import SaveRow from "./SaveRow.svelte"
+import DeleteRow from "./DeleteRow.svelte"
// defines what actions are available, when adding a new one
// the component is the setup panel for the action
@@ -11,6 +12,10 @@ export default [
name: "Save Row",
component: SaveRow,
},
+ {
+ name: "Delete Row",
+ component: DeleteRow,
+ },
{
name: "Navigate To",
component: NavigateTo,
diff --git a/packages/builder/src/components/userInterface/propertyCategories.js b/packages/builder/src/components/userInterface/propertyCategories.js
index 00b67890fb..bb35f2704c 100644
--- a/packages/builder/src/components/userInterface/propertyCategories.js
+++ b/packages/builder/src/components/userInterface/propertyCategories.js
@@ -81,6 +81,7 @@ export const layout = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
],
},
@@ -98,6 +99,7 @@ export const margin = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "128px", value: "128px" },
{ label: "256px", value: "256px" },
@@ -116,6 +118,7 @@ export const margin = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "128px", value: "128px" },
{ label: "256px", value: "256px" },
@@ -134,7 +137,10 @@ export const margin = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
+ { label: "128px", value: "128px" },
+ { label: "256px", value: "256px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
],
@@ -150,6 +156,7 @@ export const margin = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "128px", value: "128px" },
{ label: "256px", value: "256px" },
@@ -168,6 +175,7 @@ export const margin = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "128px", value: "128px" },
{ label: "256px", value: "256px" },
@@ -189,6 +197,7 @@ export const padding = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
@@ -205,6 +214,7 @@ export const padding = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
@@ -221,6 +231,7 @@ export const padding = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
@@ -237,6 +248,7 @@ export const padding = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
@@ -253,6 +265,7 @@ export const padding = [
{ label: "16px", value: "16px" },
{ label: "20px", value: "20px" },
{ label: "32px", value: "32px" },
+ { label: "48px", value: "48px" },
{ label: "64px", value: "64px" },
{ label: "Auto", value: "auto" },
{ label: "100%", value: "100%" },
diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
index 458c35116a..db1079b8ee 100644
--- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js
+++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js
@@ -6,6 +6,7 @@ import TableViewSelect from "components/userInterface/TableViewSelect.svelte"
import TableViewFieldSelect from "components/userInterface/TableViewFieldSelect.svelte"
import Event from "components/userInterface/EventsEditor/EventPropertyControl.svelte"
import ScreenSelect from "components/userInterface/ScreenSelect.svelte"
+import DetailScreenSelect from "components/userInterface/DetailScreenSelect.svelte"
import { IconSelect } from "components/userInterface/IconSelect"
import Colorpicker from "@budibase/colorpicker"
@@ -132,62 +133,23 @@ export default {
],
},
{
- name: "Input",
- description: "These components handle user input.",
+ _component: "@budibase/standard-components/input",
+ name: "Textfield",
+ description:
+ "A textfield component that allows the user to input text.",
icon: "ri-edit-box-line",
- commonProps: {},
- children: [
- {
- _component: "@budibase/standard-components/input",
- name: "Textfield",
- description:
- "A textfield component that allows the user to input text.",
- icon: "ri-edit-box-line",
- properties: {
- design: { ...all },
- settings: [
- { label: "Label", key: "label", control: Input },
- {
- label: "Type",
- key: "type",
- control: OptionSelect,
- options: ["text", "password"],
- },
- ],
+ properties: {
+ design: { ...all },
+ settings: [
+ { label: "Label", key: "label", control: Input },
+ {
+ label: "Type",
+ key: "type",
+ control: OptionSelect,
+ options: ["text", "password"],
},
- },
- {
- _component: "@budibase/standard-components/checkbox",
- name: "Checkbox",
- description: "A selectable checkbox component",
- icon: "ri-checkbox-line",
- properties: {
- design: { ...all },
- settings: [{ label: "Label", key: "label", control: Input }],
- },
- },
- {
- _component: "@budibase/standard-components/radiobutton",
- name: "Radiobutton",
- description: "A selectable radiobutton component",
- icon: "ri-radio-button-line",
- properties: {
- design: { ...all },
- settings: [{ label: "Label", key: "label", control: Input }],
- },
- },
- {
- _component: "@budibase/standard-components/select",
- name: "Select",
- description:
- "A select component for choosing from different options",
- icon: "ri-file-list-line",
- properties: {
- design: { ...all },
- settings: [],
- },
- },
- ],
+ ],
+ },
},
{
_component: "@budibase/standard-components/button",
@@ -327,6 +289,11 @@ export default {
key: "datasource",
control: TableViewSelect,
},
+ {
+ label: "Detail URL",
+ key: "detailUrl",
+ control: DetailScreenSelect,
+ },
{
label: "Editable",
key: "editable",
@@ -578,48 +545,6 @@ export default {
},
],
},
- {
- name: "Table",
- _component: "@budibase/standard-components/datatable",
- description: "A component that generates a table from your data.",
- icon: "ri-archive-drawer-line",
- properties: {
- design: { ...all },
- settings: [
- {
- label: "Data",
- key: "datasource",
- control: TableViewSelect,
- },
- {
- label: "Stripe Color",
- key: "stripeColor",
- control: Colorpicker,
- defaultValue: "#FFFFFF",
- },
- {
- label: "Border Color",
- key: "borderColor",
- control: Colorpicker,
- defaultValue: "#FFFFFF",
- },
- {
- label: "TH Color",
- key: "backgroundColor",
- control: Colorpicker,
- defaultValue: "#FFFFFF",
- },
- {
- label: "TH Font Color",
- key: "color",
- control: Colorpicker,
- defaultValue: "#FFFFFF",
- },
- { label: "Table", key: "table", control: TableSelect },
- ],
- },
- children: [],
- },
{
name: "Form",
description: "A component that generates a form from your data.",
diff --git a/packages/builder/src/pages/index.svelte b/packages/builder/src/pages/index.svelte
index 09fa40d1c7..43f3fd02d7 100644
--- a/packages/builder/src/pages/index.svelte
+++ b/packages/builder/src/pages/index.svelte
@@ -66,7 +66,7 @@
-
+
diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock
index dfeebc7214..7da63d089e 100644
--- a/packages/builder/yarn.lock
+++ b/packages/builder/yarn.lock
@@ -709,10 +709,10 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
-"@budibase/bbui@^1.41.0":
- version "1.41.0"
- resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.41.0.tgz#cb239db3071a4a6c6f0ef48ddde55f5eab9808ce"
- integrity sha512-pT5u6HDdXcylWgSE1TBt3jETg92GwgAXpUsBVqX+OUE/2lNbmThb8egAckpemHDvm91FAL0nApQYpV7c/qLzvw==
+"@budibase/bbui@^1.44.0":
+ version "1.44.0"
+ resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.44.0.tgz#0c00d98173a8b0ab757f61e349ed366bf640be4b"
+ integrity sha512-YlcRSgOZct8W07z9IaOXNFrVvG0EUWxzcfuEOfXZRviGxm9TIhe/G6T9Cai1ZgPGicnKXa0dPAT3UrzIVB5xJg==
dependencies:
sirv-cli "^0.4.6"
svelte-flatpickr "^2.4.0"
diff --git a/packages/cli/package.json b/packages/cli/package.json
index e3a1fc1f3a..0d6ab00d63 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "budibase",
- "version": "0.2.1",
+ "version": "0.2.2",
"description": "Budibase CLI",
"repository": "https://github.com/Budibase/Budibase",
"homepage": "https://www.budibase.com",
@@ -17,7 +17,7 @@
"author": "Budibase",
"license": "AGPL-3.0-or-later",
"dependencies": {
- "@budibase/server": "^0.2.1",
+ "@budibase/server": "^0.2.2",
"@inquirer/password": "^0.0.6-alpha.0",
"chalk": "^2.4.2",
"dotenv": "^8.2.0",
diff --git a/packages/client/package.json b/packages/client/package.json
index b4313a9954..e9a4e52c8f 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/client",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "MPL-2.0",
"main": "dist/budibase-client.js",
"module": "dist/budibase-client.esm.mjs",
diff --git a/packages/client/src/api/index.js b/packages/client/src/api/index.js
index 6082accb51..7d7c9b52c0 100644
--- a/packages/client/src/api/index.js
+++ b/packages/client/src/api/index.js
@@ -1,5 +1,5 @@
import { authenticate } from "./authenticate"
-import appStore from "../state/store"
+// import appStore from "../state/store"
const apiCall = method => async ({ url, body }) => {
const response = await fetch(url, {
@@ -37,7 +37,7 @@ const del = apiCall("DELETE")
const ERROR_MEMBER = "##error"
const error = message => {
const err = { [ERROR_MEMBER]: message }
- appStore.update(s => s["##error_message"], message)
+ // appStore.update(s => s["##error_message"], message)
return err
}
@@ -67,6 +67,11 @@ const updateRow = async (params, state) => {
})
}
+const deleteRow = async params =>
+ await del({
+ url: `/api/${params.tableId}/rows/${params.rowId}/${params.revId}`,
+ })
+
const makeRowRequestBody = (parameters, state) => {
// start with the row thats currently in context
const body = { ...(state.data || {}) }
@@ -103,4 +108,5 @@ export default {
authenticate: authenticate(apiOpts),
saveRow,
updateRow,
+ deleteRow,
}
diff --git a/packages/client/src/render/screenRouter.js b/packages/client/src/render/screenRouter.js
index 6af4f5491a..0c045097ed 100644
--- a/packages/client/src/render/screenRouter.js
+++ b/packages/client/src/render/screenRouter.js
@@ -3,27 +3,48 @@ import appStore from "../state/store"
import { parseAppIdFromCookie } from "./getAppId"
export const screenRouter = ({ screens, onScreenSelected, window }) => {
- const makeRootedPath = url => {
- const hostname = window.location && window.location.hostname
- if (hostname) {
- if (
- hostname === "localhost" ||
- hostname === "127.0.0.1" ||
- hostname.startsWith("192.168")
- ) {
- const appId = parseAppIdFromCookie(window.document.cookie)
- if (url) {
- if (url.startsWith(appId)) return url
- return `/${appId}${url.startsWith("/") ? "" : "/"}${url}`
- }
- return appId
- }
- }
+ function sanitize(url) {
+ if (!url) return url
return url
+ .split("/")
+ .map(part => {
+ // if parameter, then use as is
+ if (part.startsWith(":")) return part
+ return encodeURIComponent(part)
+ })
+ .join("/")
+ .toLowerCase()
+ }
+
+ const isRunningLocally = () => {
+ const hostname = (window.location && window.location.hostname) || ""
+ return (
+ hostname === "localhost" ||
+ hostname === "127.0.0.1" ||
+ hostname.startsWith("192.168")
+ )
+ }
+
+ const makeRootedPath = url => {
+ if (isRunningLocally()) {
+ const appId = parseAppIdFromCookie(window.document.cookie)
+ if (url) {
+ url = sanitize(url)
+ if (!url.startsWith("/")) {
+ url = `/${url}`
+ }
+ if (url.startsWith(`/${appId}`)) {
+ return url
+ }
+ return `/${appId}${url}`
+ }
+ return `/${appId}`
+ }
+ return sanitize(url)
}
const routes = screens.map(s => makeRootedPath(s.route))
- let fallback = routes.findIndex(([p]) => p === "*")
+ let fallback = routes.findIndex(([p]) => p === makeRootedPath("*"))
if (fallback < 0) fallback = 0
let current
@@ -32,7 +53,7 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => {
const _url = makeRootedPath(url.state || url)
current = routes.findIndex(
p =>
- p !== "*" &&
+ p !== makeRootedPath("*") &&
new RegExp("^" + p.toLowerCase() + "$").test(_url.toLowerCase())
)
@@ -40,6 +61,8 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => {
if (current === -1) {
routes.forEach((p, i) => {
+ // ignore home - which matched everything
+ if (p === makeRootedPath("*")) return
const pm = regexparam(p)
const matches = pm.pattern.exec(_url)
diff --git a/packages/client/src/state/eventHandlers.js b/packages/client/src/state/eventHandlers.js
index 629631d001..7461913929 100644
--- a/packages/client/src/state/eventHandlers.js
+++ b/packages/client/src/state/eventHandlers.js
@@ -8,6 +8,7 @@ export const eventHandlers = routeTo => {
"Navigate To": param => routeTo(param && param.url),
"Update Row": api.updateRow,
"Save Row": api.saveRow,
+ "Delete Row": api.deleteRow,
"Trigger Workflow": api.triggerWorkflow,
}
diff --git a/packages/server/package.json b/packages/server/package.json
index de1ccd5845..93194a1080 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/server",
- "version": "0.2.1",
+ "version": "0.2.2",
"description": "Budibase Web Server",
"main": "src/electron.js",
"repository": {
@@ -42,7 +42,7 @@
"author": "Michael Shanks",
"license": "AGPL-3.0-or-later",
"dependencies": {
- "@budibase/client": "^0.2.1",
+ "@budibase/client": "^0.2.2",
"@koa/router": "^8.0.0",
"@sendgrid/mail": "^7.1.1",
"@sentry/node": "^5.19.2",
diff --git a/packages/server/src/api/controllers/table.js b/packages/server/src/api/controllers/table.js
index 82f981e3e6..3030274016 100644
--- a/packages/server/src/api/controllers/table.js
+++ b/packages/server/src/api/controllers/table.js
@@ -41,6 +41,18 @@ exports.save = async function(ctx) {
oldTable = await db.get(ctx.request.body._id)
}
+ // make sure that types don't change of a column, have to remove
+ // the column if you want to change the type
+ if (oldTable && oldTable.schema) {
+ for (let propKey of Object.keys(tableToSave.schema)) {
+ let column = tableToSave.schema[propKey]
+ let oldColumn = oldTable.schema[propKey]
+ if (oldColumn && oldColumn.type !== column.type) {
+ ctx.throw(400, "Cannot change the type of a column")
+ }
+ }
+ }
+
// Don't rename if the name is the same
let { _rename } = tableToSave
if (_rename && _rename.old === _rename.updated) {
@@ -50,9 +62,9 @@ exports.save = async function(ctx) {
// rename row fields when table column is renamed
if (_rename && tableToSave.schema[_rename.updated].type === "link") {
- throw "Cannot rename a linked field."
+ ctx.throw(400, "Cannot rename a linked column.")
} else if (_rename && tableToSave.primaryDisplay === _rename.old) {
- throw "Cannot rename the display column."
+ ctx.throw(400, "Cannot rename the display column.")
} else if (_rename) {
const rows = await db.allDocs(
getRowParams(tableToSave._id, null, {
diff --git a/packages/server/src/api/index.js b/packages/server/src/api/index.js
index 5d5f64459e..0a4de71135 100644
--- a/packages/server/src/api/index.js
+++ b/packages/server/src/api/index.js
@@ -51,6 +51,7 @@ router
process.env.NODE_ENV !== "cypress"
await next()
})
+ .use("/health", ctx => (ctx.status = 200))
.use(authenticated)
// error handling middleware
diff --git a/packages/server/src/db/linkedRows/LinkController.js b/packages/server/src/db/linkedRows/LinkController.js
index 06e4aab1f2..5eed0bb4a8 100644
--- a/packages/server/src/db/linkedRows/LinkController.js
+++ b/packages/server/src/db/linkedRows/LinkController.js
@@ -1,6 +1,7 @@
const CouchDB = require("../index")
const { IncludeDocs, getLinkDocuments } = require("./linkUtils")
const { generateLinkID } = require("../utils")
+const Sentry = require("@sentry/node")
/**
* Creates a new link document structure which can be put to the database. It is important to
@@ -289,10 +290,14 @@ class LinkController {
const schema = table.schema
for (let fieldName of Object.keys(schema)) {
const field = schema[fieldName]
- if (field.type === "link") {
- const linkedTable = await this._db.get(field.tableId)
- delete linkedTable.schema[table.name]
- await this._db.put(linkedTable)
+ try {
+ if (field.type === "link") {
+ const linkedTable = await this._db.get(field.tableId)
+ delete linkedTable.schema[field.fieldName]
+ await this._db.put(linkedTable)
+ }
+ } catch (err) {
+ Sentry.captureException(err)
}
}
// need to get the full link docs to delete them
diff --git a/packages/server/src/db/linkedRows/index.js b/packages/server/src/db/linkedRows/index.js
index 0932fc665f..6f745b6e7c 100644
--- a/packages/server/src/db/linkedRows/index.js
+++ b/packages/server/src/db/linkedRows/index.js
@@ -1,5 +1,10 @@
const LinkController = require("./LinkController")
-const { IncludeDocs, getLinkDocuments, createLinkView } = require("./linkUtils")
+const {
+ IncludeDocs,
+ getLinkDocuments,
+ createLinkView,
+ getUniqueByProp,
+} = require("./linkUtils")
const _ = require("lodash")
/**
@@ -110,7 +115,12 @@ exports.attachLinkInfo = async (instanceId, rows) => {
// now iterate through the rows and all field information
for (let row of rows) {
// get all links for row, ignore fieldName for now
- const linkVals = responses.filter(el => el.thisId === row._id)
+ // have to get unique as the previous table query can
+ // return duplicates, could be querying for both tables in a relation
+ const linkVals = getUniqueByProp(
+ responses.filter(el => el.thisId === row._id),
+ "id"
+ )
for (let linkVal of linkVals) {
// work out which link pertains to this row
if (!(row[linkVal.fieldName] instanceof Array)) {
diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js
index 8f548eed3d..5f600cc3cb 100644
--- a/packages/server/src/db/linkedRows/linkUtils.js
+++ b/packages/server/src/db/linkedRows/linkUtils.js
@@ -92,3 +92,9 @@ exports.getLinkDocuments = async function({
}
}
}
+
+exports.getUniqueByProp = (array, prop) => {
+ return array.filter((obj, pos, arr) => {
+ return arr.map(mapObj => mapObj[prop]).indexOf(obj[prop]) === pos
+ })
+}
diff --git a/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json b/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
index 436f5b32df..44d30a65f1 100644
--- a/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
+++ b/packages/server/src/utilities/appDirectoryTemplate/pages/main/page.json
@@ -19,7 +19,8 @@
"justify-content": "flex-start",
"align-items": "flex-start",
"background": "#fff",
- "width": "100%"
+ "width": "100%",
+ "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)"
},
"hover": {},
"active": {},
@@ -67,7 +68,7 @@
"_styles": {
"normal": {
"font-family": "Inter",
- "font-weight": "400",
+ "font-weight": "500",
"color": "#000000",
"text-decoration-line": "none",
"font-size": "16px"
diff --git a/packages/server/src/utilities/appDirectoryTemplate/pages/main/screens/d834fea2-1b3e-4320-ab34-f9009f5ecc59.json b/packages/server/src/utilities/appDirectoryTemplate/pages/main/screens/d834fea2-1b3e-4320-ab34-f9009f5ecc59.json
index 7c94af517e..5afcf244f2 100644
--- a/packages/server/src/utilities/appDirectoryTemplate/pages/main/screens/d834fea2-1b3e-4320-ab34-f9009f5ecc59.json
+++ b/packages/server/src/utilities/appDirectoryTemplate/pages/main/screens/d834fea2-1b3e-4320-ab34-f9009f5ecc59.json
@@ -97,6 +97,6 @@
],
"_instanceName": "Home"
},
- "route": "/*",
+ "route": "/",
"name": "d834fea2-1b3e-4320-ab34-f9009f5ecc59"
}
diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json
index 7d64f49c84..d19b2ad85f 100644
--- a/packages/standard-components/components.json
+++ b/packages/standard-components/components.json
@@ -240,12 +240,13 @@
},
"height": {
"type": "number",
- "default": "500"
+ "default": "540"
},
"pagination": {
"type": "bool",
"default": true
- }
+ },
+ "detailUrl": "string"
}
},
"dataform": {
diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json
index 77e1cee746..251f9f605c 100644
--- a/packages/standard-components/package.json
+++ b/packages/standard-components/package.json
@@ -13,7 +13,7 @@
"dev:builder": "rollup -cw"
},
"devDependencies": {
- "@budibase/client": "^0.2.1",
+ "@budibase/client": "^0.2.2",
"@rollup/plugin-commonjs": "^11.1.0",
"lodash": "^4.17.15",
"rollup": "^2.11.2",
@@ -31,12 +31,12 @@
"keywords": [
"svelte"
],
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "MIT",
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691",
"dependencies": {
"@beyonk/svelte-googlemaps": "^2.2.0",
- "@budibase/bbui": "^1.41.0",
+ "@budibase/bbui": "^1.44.0",
"@budibase/svelte-ag-grid": "^0.0.13",
"@fortawesome/fontawesome-free": "^5.14.0",
"@svelteschool/svelte-forms": "^0.7.0",
diff --git a/packages/standard-components/src/Button.svelte b/packages/standard-components/src/Button.svelte
index 7441f478d1..54f7812dc2 100644
--- a/packages/standard-components/src/Button.svelte
+++ b/packages/standard-components/src/Button.svelte
@@ -18,19 +18,17 @@
bind:this={theButton}
class="default"
disabled={disabled || false}
- on:click={clickHandler}>
+ on:click|once={clickHandler}>
{#if !_bb.props._children || _bb.props._children.length === 0}{text}{/if}
diff --git a/packages/standard-components/src/DataGrid/customRenderer.js b/packages/standard-components/src/DataGrid/customRenderer.js
index 607a4f8432..d54e91f623 100644
--- a/packages/standard-components/src/DataGrid/customRenderer.js
+++ b/packages/standard-components/src/DataGrid/customRenderer.js
@@ -2,6 +2,7 @@
// https://www.ag-grid.com/javascript-grid-cell-rendering-components/
import AttachmentCell from "./AttachmentCell/Button.svelte"
+import ViewDetails from "./ViewDetails/Cell.svelte"
import Select from "./Select/Wrapper.svelte"
import DatePicker from "./DateTime/Wrapper.svelte"
import RelationshipDisplay from "./Relationship/RelationshipDisplay.svelte"
@@ -11,18 +12,23 @@ const renderers = new Map([
["attachment", attachmentRenderer],
["options", optionsRenderer],
["link", linkedRowRenderer],
+ ["_id", viewDetailsRenderer],
])
-export function getRenderer({ type, constraints }, editable) {
- if (renderers.get(type)) {
- return renderers.get(type)(constraints, editable)
+export function getRenderer(schema, editable) {
+ if (renderers.get(schema.type)) {
+ return renderers.get(schema.type)(
+ schema.options,
+ schema.constraints,
+ editable
+ )
} else {
return false
}
}
/* eslint-disable no-unused-vars */
-function booleanRenderer(constraints, editable) {
+function booleanRenderer(options, constraints, editable) {
return params => {
const toggle = e => {
params.value = !params.value
@@ -44,7 +50,7 @@ function booleanRenderer(constraints, editable) {
}
}
/* eslint-disable no-unused-vars */
-function attachmentRenderer(constraints, editable) {
+function attachmentRenderer(options, constraints, editable) {
return params => {
const container = document.createElement("div")
@@ -66,7 +72,7 @@ function attachmentRenderer(constraints, editable) {
}
}
/* eslint-disable no-unused-vars */
-function dateRenderer(constraints, editable) {
+function dateRenderer(options, constraints, editable) {
return function(params) {
const container = document.createElement("div")
const toggle = e => {
@@ -74,8 +80,7 @@ function dateRenderer(constraints, editable) {
}
// Options need to be passed in with minTime and maxTime! Needs bbui update.
-
- const datePickerInstance = new DatePicker({
+ new DatePicker({
target: container,
props: {
value: params.value,
@@ -86,7 +91,7 @@ function dateRenderer(constraints, editable) {
}
}
-function optionsRenderer({ inclusion }, editable) {
+function optionsRenderer(options, constraints, editable) {
return params => {
if (!editable) return params.value
const container = document.createElement("div")
@@ -101,7 +106,7 @@ function optionsRenderer({ inclusion }, editable) {
target: container,
props: {
value: params.value,
- options: inclusion,
+ options: constraints.inclusion,
},
})
@@ -111,7 +116,7 @@ function optionsRenderer({ inclusion }, editable) {
}
}
/* eslint-disable no-unused-vars */
-function linkedRowRenderer(constraints, editable) {
+function linkedRowRenderer(options, constraints, editable) {
return params => {
let container = document.createElement("div")
container.style.display = "grid"
@@ -129,3 +134,25 @@ function linkedRowRenderer(constraints, editable) {
return container
}
}
+
+/* eslint-disable no-unused-vars */
+function viewDetailsRenderer(options, constraints, editable) {
+ return params => {
+ let container = document.createElement("div")
+ container.style.display = "grid"
+ container.style.alignItems = "center"
+ container.style.height = "100%"
+
+ let url = "/"
+ if (options.detailUrl) {
+ url = options.detailUrl.replace(":id", params.data._id)
+ }
+
+ new ViewDetails({
+ target: container,
+ props: { url },
+ })
+
+ return container
+ }
+}
diff --git a/packages/standard-components/src/Form.svelte b/packages/standard-components/src/Form.svelte
index 29401e8ad4..a95d64d35c 100644
--- a/packages/standard-components/src/Form.svelte
+++ b/packages/standard-components/src/Form.svelte
@@ -23,9 +23,7 @@
{#each fields as field}
{#if !(schema[field].type === 'boolean' && !wide)}
-
+
{/if}
{#if schema[field].type === 'options'}
-
-
@@ -91,25 +89,24 @@
.content {
display: flex;
flex-direction: column;
- align-items: center;
+ align-items: stretch;
justify-content: center;
}
.logo-container {
margin-bottom: 10px;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
}
.logo-container > img {
- height: 80px;
+ max-height: 80px;
max-width: 200px;
margin-bottom: 20px;
}
- .login-button-container {
- margin-top: 6px;
- max-width: 100%;
- }
-
.header-content {
font-family: Inter;
font-weight: 700;
@@ -137,12 +134,13 @@
.form-root {
display: flex;
flex-direction: column;
- align-items: center;
+ align-items: stretch;
width: 300px;
+ margin: auto;
+ gap: 8px;
}
.control {
- padding: 6px 0px;
width: 100%;
}
@@ -186,4 +184,9 @@
border-color: #393c44;
color: #393c44;
}
+
+ h2 {
+ text-align: center;
+ margin-bottom: 10px;
+ }
diff --git a/packages/standard-components/src/Text.svelte b/packages/standard-components/src/Text.svelte
index 55b8fb762b..d1b5c4a567 100644
--- a/packages/standard-components/src/Text.svelte
+++ b/packages/standard-components/src/Text.svelte
@@ -1,11 +1,7 @@