diff --git a/packages/builder/package.json b/packages/builder/package.json
index ff249c0e40..9dab2460fb 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -81,7 +81,8 @@
"shortid": "^2.2.15",
"svelte-loading-spinners": "^0.1.1",
"svelte-portal": "^0.1.0",
- "yup": "^0.29.2"
+ "yup": "^0.29.2",
+ "uuid": "^8.3.1"
},
"devDependencies": {
"@babel/core": "^7.5.5",
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 31b8dcff22..2c1def6efb 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -50,6 +50,7 @@ export const getFrontendStore = () => {
return state
})
const screens = await api.get("/api/screens").then(r => r.json())
+ const routing = await api.get("/api/routing").then(r => r.json())
const mainScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.main._id)
diff --git a/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js
index a8ab27df3d..b25562758e 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/createFromScratchScreen.js
@@ -1,22 +1,13 @@
+import { Screen } from "./utils/Screen"
+
export default {
name: `Create from scratch`,
create: () => createScreen(),
}
-const createScreen = () => ({
- props: {
- _id: "",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- type: "div",
- _children: [],
- _instanceName: "",
- },
- route: "",
- name: "screen-id",
-})
+const createScreen = () => {
+ return new Screen()
+ .mainType("div")
+ .component("@budibase/standard-components/container")
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/emptyNewRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/emptyNewRowScreen.js
index e58319688b..a2f2f6df67 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/emptyNewRowScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/emptyNewRowScreen.js
@@ -1,22 +1,13 @@
+import { Screen } from "./utils/Screen"
+
export default {
name: `New Row (Empty)`,
create: () => createScreen(),
}
-const createScreen = () => ({
- props: {
- _id: "",
- _component: "@budibase/standard-components/newrow",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _children: [],
- _instanceName: "",
- table: "",
- },
- route: "",
- name: "screen-id",
-})
+const createScreen = () => {
+ return new Screen()
+ .component("@budibase/standard-components/newrow")
+ .table("")
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/emptyRowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/emptyRowDetailScreen.js
index a75de583cb..5dbdcf4e69 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/emptyRowDetailScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/emptyRowDetailScreen.js
@@ -1,22 +1,13 @@
+import { Screen } from "./utils/Screen"
+
export default {
name: `Row Detail (Empty)`,
create: () => createScreen(),
}
-const createScreen = () => ({
- props: {
- _id: "",
- _component: "@budibase/standard-components/rowdetail",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- _children: [],
- _instanceName: "",
- table: "",
- },
- route: "",
- name: "screen-id",
-})
+const createScreen = () => {
+ return new Screen()
+ .component("@budibase/standard-components/rowdetail")
+ .table("")
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/index.js b/packages/builder/src/builderStore/store/screenTemplates/index.js
index 5abe428966..ddf48cbe44 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/index.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/index.js
@@ -24,7 +24,7 @@ const createTemplateOverride = (frontendState, create) => () => {
}
screen.props._id = uuid()
screen.name = screen.props._id
- screen.route = screen.route.toLowerCase()
+ screen.routing.route = screen.routing.route.toLowerCase()
return screen
}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
index 50e90cddcf..1e699dad95 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/newRowScreen.js
@@ -1,5 +1,12 @@
-import sanitizeUrl from "./sanitizeUrl"
-import { rowListUrl } from "./rowListScreen"
+import sanitizeUrl from "./utils/sanitizeUrl"
+import { Component } from "./utils/Component"
+import { Screen } from "./utils/Screen"
+import {
+ makeBreadcrumbContainer,
+ makeMainContainer,
+ makeTitleContainer,
+ makeSaveButton,
+} from "./utils/commonComponents"
export default function(tables) {
return tables.map(table => {
@@ -14,242 +21,26 @@ export default function(tables) {
export const newRowUrl = table => sanitizeUrl(`/${table.name}/new`)
export const NEW_ROW_TEMPLATE = "NEW_ROW_TEMPLATE"
-const createScreen = table => ({
- props: {
- _id: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
- _component: "@budibase/standard-components/newrow",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- table: table._id,
- _children: [
- {
- _id: "ccad6cc135c7947a7ba9c631f655d6e0f",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- 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: {},
- selected: {},
- },
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Container",
- _children: [
- {
- _id: "c6e91622ba7984f468f70bf4bf5120246",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- "font-size": "14px",
- color: "#757575",
- },
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Breadcrumbs",
- _children: [
- {
- _id: "caa33353c252c4931b2a51b48a559a7fc",
- _component: "@budibase/standard-components/link",
- _styles: {
- normal: {
- color: "#757575",
- "text-transform": "capitalize",
- },
- hover: {
- color: "#4285f4",
- },
- active: {},
- selected: {},
- },
- _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: [],
- },
- ],
- },
- {
- _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: {},
- active: {},
- selected: {},
- },
- _code: "",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Form",
- _children: [],
- },
- ],
- },
- ],
- _instanceName: `${table.name} - New`,
- _code: "",
- },
- route: newRowUrl(table),
- name: "",
-})
+function generateTitleContainer(table) {
+ return makeTitleContainer("New Row").addChild(makeSaveButton(table))
+}
+
+const createScreen = table => {
+ const dataform = new Component(
+ "@budibase/standard-components/dataformwide"
+ ).instanceName("Form")
+
+ const container = makeMainContainer()
+ .addChild(makeBreadcrumbContainer(table.name, "New"))
+ .addChild(generateTitleContainer(table))
+ .addChild(dataform)
+
+ return new Screen()
+ .component("@budibase/standard-components/newrow")
+ .table(table._id)
+ .route(newRowUrl(table))
+ .instanceName(`${table.name} - New`)
+ .name("")
+ .addChild(container)
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
index a4f55f2fd1..526f457f3e 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
@@ -1,5 +1,13 @@
-import sanitizeUrl from "./sanitizeUrl"
+import sanitizeUrl from "./utils/sanitizeUrl"
import { rowListUrl } from "./rowListScreen"
+import { Screen } from "./utils/Screen"
+import { Component } from "./utils/Component"
+import {
+ makeMainContainer,
+ makeBreadcrumbContainer,
+ makeTitleContainer,
+ makeSaveButton,
+} from "./utils/commonComponents"
export default function(tables) {
return tables.map(table => {
@@ -17,288 +25,78 @@ 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: "c683c4ca8ffc849c6bdd3b7d637fbbf3c",
- _component: "@budibase/standard-components/rowdetail",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- table: table._id,
- _children: [
- {
- _id: "ccad6cc135c7947a7ba9c631f655d6e0f",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- 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",
+function generateTitleContainer(table, title) {
+ // have to override style for this, its missing margin
+ const saveButton = makeSaveButton(table).normalStyle({
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ })
+
+ const deleteButton = new Component("@budibase/standard-components/button")
+ .normalStyle({
+ 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",
+ })
+ .hoverStyle({
+ background: "transparent",
+ color: "#4285f4",
+ })
+ .text("Delete")
+ .customProps({
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ rowId: "{{ data._id }}",
+ revId: "{{ data._rev }}",
+ tableId: table._id,
},
- hover: {},
- active: {},
- selected: {},
+ "##eventHandlerType": "Delete Row",
},
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Container",
- _children: [
- {
- _id: "c6e91622ba7984f468f70bf4bf5120246",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- "font-size": "14px",
- color: "#757575",
- },
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Breadcrumbs",
- _children: [
- {
- _id: "caa33353c252c4931b2a51b48a559a7fc",
- _component: "@budibase/standard-components/link",
- _styles: {
- normal: {
- color: "#757575",
- "text-transform": "capitalize",
- },
- hover: {
- color: "#4285f4",
- },
- active: {},
- selected: {},
- },
- _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: [],
- },
- ],
+ {
+ parameters: {
+ url: rowListUrl(table),
},
- {
- _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: {},
- active: {},
- selected: {},
- },
- _code: "",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Form",
- _children: [],
- },
- ],
- },
- ],
- _instanceName: `${table.name} - Detail`,
- _code: "",
- },
- route: rowDetailUrl(table),
- name: "",
-})
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ })
+ .instanceName("Delete Button")
+
+ return makeTitleContainer(title)
+ .addChild(deleteButton)
+ .addChild(saveButton)
+}
+
+const createScreen = (table, heading) => {
+ const dataform = new Component(
+ "@budibase/standard-components/dataformwide"
+ ).instanceName("Form")
+
+ const container = makeMainContainer()
+ .addChild(makeBreadcrumbContainer(table.name, heading || "Edit"))
+ .addChild(generateTitleContainer(table, heading || "Edit Row"))
+ .addChild(dataform)
+
+ return new Screen()
+ .component("@budibase/standard-components/rowdetail")
+ .table(table._id)
+ .instanceName(`${table.name} - Detail`)
+ .route(rowDetailUrl(table))
+ .name("")
+ .addChild(container)
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
index 5c71a45f1f..54a066af9c 100644
--- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
+++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js
@@ -1,5 +1,7 @@
-import sanitizeUrl from "./sanitizeUrl"
+import sanitizeUrl from "./utils/sanitizeUrl"
import { newRowUrl } from "./newRowScreen"
+import { Screen } from "./utils/Screen"
+import { Component } from "./utils/Component"
export default function(tables) {
return tables.map(table => {
@@ -14,159 +16,103 @@ export default function(tables) {
export const ROW_LIST_TEMPLATE = "ROW_LIST_TEMPLATE"
export const rowListUrl = table => sanitizeUrl(`/${table.name}`)
-const createScreen = table => ({
- props: {
- _id: "c7365379815e4457dbe703a886c2da43b",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {},
- hover: {},
- active: {},
- selected: {},
- },
- type: "div",
- _children: [
- {
- _id: "cf51241fc063d4d87be032dd509fe0244",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- 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",
+function generateTitleContainer(table) {
+ const newButton = new Component("@budibase/standard-components/button")
+ .normalStyle({
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ })
+ .hoverStyle({
+ background: "#4285f4",
+ })
+ .text("Create New")
+ .customProps({
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ url: newRowUrl(table),
},
- hover: {},
- active: {},
- selected: {},
+ "##eventHandlerType": "Navigate To",
},
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Container",
- _children: [
- {
- _id: "c73294c301fd145aabe9bbbbd96a150ac",
- _component: "@budibase/standard-components/container",
- _styles: {
- normal: {
- display: "flex",
- "flex-direction": "row",
- "justify-content": "space-between",
- "align-items": "center",
- "margin-bottom": "32px",
- },
- hover: {},
- active: {},
- selected: {},
- },
- _code: "",
- className: "",
- onLoad: [],
- type: "div",
- _instanceId: "inst_app_8fb_631af42f9dc94da2b5c48dc6c5124610",
- _instanceName: "Title Container",
- _children: [
- {
- _id: "c2b77901df95a4d1ca7204c58300bc94b",
- _component: "@budibase/standard-components/heading",
- _styles: {
- normal: {
- margin: "0px",
- flex: "1 1 auto",
- "text-transform": "capitalize",
- },
- hover: {},
- active: {},
- selected: {},
- },
- _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: [],
- },
- ],
- },
- {
- _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`,
- },
- ],
+ ],
+ })
+ .instanceName("New Button")
+
+ const heading = new Component("@budibase/standard-components/heading")
+ .normalStyle({
+ margin: "0px",
+ flex: "1 1 auto",
+ "text-transform": "capitalize",
+ })
+ .type("h3")
+ .instanceName("Title")
+ .text(table.name)
+
+ return new Component("@budibase/standard-components/container")
+ .type("div")
+ .normalStyle({
+ display: "flex",
+ "flex-direction": "row",
+ "justify-content": "space-between",
+ "align-items": "center",
+ "margin-bottom": "32px",
+ })
+ .instanceName("Title Container")
+ .addChild(heading)
+ .addChild(newButton)
+}
+
+const createScreen = table => {
+ const datagrid = new Component("@budibase/standard-components/datagrid")
+ .customProps({
+ datasource: {
+ label: table.name,
+ name: `all_${table._id}`,
+ tableId: table._id,
+ type: "table",
},
- ],
- _instanceName: `${table.name} - List`,
- _code: "",
- className: "",
- onLoad: [],
- },
- route: rowListUrl(table),
- name: "",
-})
+ editable: false,
+ theme: "alpine",
+ height: "540",
+ pagination: true,
+ detailUrl: `${table.name.toLowerCase()}/:id`,
+ })
+ .instanceName("Grid")
+
+ const mainContainer = new Component("@budibase/standard-components/container")
+ .normalStyle({
+ 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",
+ })
+ .type("div")
+ .instanceName("Container")
+ .addChild(generateTitleContainer(table))
+ .addChild(datagrid)
+
+ return new Screen()
+ .component("@budibase/standard-components/container")
+ .mainType("div")
+ .route(rowListUrl(table))
+ .instanceName(`${table.name} - List`)
+ .name("")
+ .addChild(mainContainer)
+ .json()
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/BaseStructure.js b/packages/builder/src/builderStore/store/screenTemplates/utils/BaseStructure.js
new file mode 100644
index 0000000000..71daca9d1b
--- /dev/null
+++ b/packages/builder/src/builderStore/store/screenTemplates/utils/BaseStructure.js
@@ -0,0 +1,35 @@
+import { cloneDeep } from "lodash/fp"
+
+export class BaseStructure {
+ constructor(isScreen) {
+ this._isScreen = isScreen
+ this._children = []
+ this._json = {}
+ }
+
+ addChild(child) {
+ this._children.push(child)
+ return this
+ }
+
+ customProps(props) {
+ for (let key of Object.keys(props)) {
+ this._json[key] = props[key]
+ }
+ return this
+ }
+
+ json() {
+ const structure = cloneDeep(this._json)
+ if (this._children.length !== 0) {
+ for (let child of this._children) {
+ if (this._isScreen) {
+ structure.props._children.push(child.json())
+ } else {
+ structure._children.push(child.json())
+ }
+ }
+ }
+ return structure
+ }
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/Component.js b/packages/builder/src/builderStore/store/screenTemplates/utils/Component.js
new file mode 100644
index 0000000000..27b4af2d5b
--- /dev/null
+++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Component.js
@@ -0,0 +1,52 @@
+import { cloneDeep } from "lodash/fp"
+import { v4 } from "uuid"
+import { BaseStructure } from "./BaseStructure"
+
+export class Component extends BaseStructure {
+ constructor(name) {
+ super(false)
+ this._children = []
+ this._json = {
+ _id: v4(),
+ _component: name,
+ _styles: {
+ normal: {},
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _code: "",
+ className: "",
+ onLoad: [],
+ type: "",
+ _instanceName: "",
+ _children: [],
+ }
+ }
+
+ type(type) {
+ this._json.type = type
+ return this
+ }
+
+ normalStyle(styling) {
+ this._json._styles.normal = styling
+ return this
+ }
+
+ hoverStyle(styling) {
+ this._json._styles.hover = styling
+ return this
+ }
+
+ text(text) {
+ this._json.text = text
+ return this
+ }
+
+ // TODO: do we need this
+ instanceName(name) {
+ this._json._instanceName = name
+ return this
+ }
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js
new file mode 100644
index 0000000000..951e26aeb6
--- /dev/null
+++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js
@@ -0,0 +1,56 @@
+import { BaseStructure } from "./BaseStructure"
+
+export class Screen extends BaseStructure {
+ constructor() {
+ super(true)
+ this._json = {
+ props: {
+ _id: "",
+ _component: "",
+ _styles: {
+ normal: {},
+ hover: {},
+ active: {},
+ selected: {},
+ },
+ _children: [],
+ _instanceName: "",
+ },
+ routing: {
+ route: "",
+ accessLevelId: "",
+ },
+ name: "screen-id",
+ }
+ }
+
+ component(name) {
+ this._json.props._component = name
+ return this
+ }
+
+ table(tableName) {
+ this._json.props.table = tableName
+ return this
+ }
+
+ mainType(type) {
+ this._json.type = type
+ return this
+ }
+
+ route(route) {
+ this._json.routing.route = route
+ return this
+ }
+
+ name(name) {
+ this._json.name = name
+ return this
+ }
+
+ instanceName(name) {
+ this._json.props._instanceName = name
+ return this
+ }
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
new file mode 100644
index 0000000000..89e08cecdb
--- /dev/null
+++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js
@@ -0,0 +1,145 @@
+import { Component } from "./Component"
+import { rowListUrl } from "../rowListScreen"
+
+export function makeLinkComponent(tableName) {
+ return new Component("@budibase/standard-components/link")
+ .normalStyle({
+ color: "#757575",
+ "text-transform": "capitalize",
+ })
+ .hoverStyle({
+ color: "#4285f4",
+ })
+ .text(tableName)
+ .customProps({
+ url: `/${tableName.toLowerCase()}`,
+ openInNewTab: false,
+ color: "",
+ hoverColor: "",
+ underline: false,
+ fontSize: "",
+ fontFamily: "initial",
+ })
+}
+
+export function makeMainContainer() {
+ return new Component("@budibase/standard-components/container")
+ .type("div")
+ .normalStyle({
+ 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",
+ })
+ .instanceName("Container")
+}
+
+export function makeBreadcrumbContainer(tableName, text, capitalise = false) {
+ const link = makeLinkComponent(tableName).instanceName("Back Link")
+
+ const arrowText = new Component("@budibase/standard-components/text")
+ .type("none")
+ .normalStyle({
+ "margin-right": "4px",
+ "margin-left": "4px",
+ })
+ .text(">")
+ .instanceName("Arrow")
+
+ const textStyling = {
+ color: "#000000",
+ }
+ if (capitalise) {
+ textStyling["text-transform"] = "capitalize"
+ }
+ const identifierText = new Component("@budibase/standard-components/text")
+ .type("none")
+ .normalStyle(textStyling)
+ .text(text)
+ .instanceName("Identifier")
+
+ return new Component("@budibase/standard-components/container")
+ .type("div")
+ .normalStyle({
+ "font-size": "14px",
+ color: "#757575",
+ })
+ .instanceName("Breadcrumbs")
+ .addChild(link)
+ .addChild(arrowText)
+ .addChild(identifierText)
+}
+
+export function makeSaveButton(table) {
+ return new Component("@budibase/standard-components/button")
+ .normalStyle({
+ background: "#000000",
+ "border-width": "0",
+ "border-style": "None",
+ color: "#fff",
+ "font-family": "Inter",
+ "font-weight": "500",
+ "font-size": "14px",
+ "margin-left": "16px",
+ })
+ .hoverStyle({
+ background: "#4285f4",
+ })
+ .text("Save")
+ .customProps({
+ className: "",
+ disabled: false,
+ onClick: [
+ {
+ parameters: {
+ contextPath: "data",
+ tableId: table._id,
+ },
+ "##eventHandlerType": "Save Row",
+ },
+ {
+ parameters: {
+ url: rowListUrl(table),
+ },
+ "##eventHandlerType": "Navigate To",
+ },
+ ],
+ })
+ .instanceName("Save Button")
+}
+
+export function makeTitleContainer(title) {
+ const heading = new Component("@budibase/standard-components/heading")
+ .normalStyle({
+ margin: "0px",
+ "margin-bottom": "0px",
+ "margin-right": "0px",
+ "margin-top": "0px",
+ "margin-left": "0px",
+ flex: "1 1 auto",
+ })
+ .type("h3")
+ .instanceName("Title")
+ .text(title)
+
+ return new Component("@budibase/standard-components/container")
+ .type("div")
+ .normalStyle({
+ display: "flex",
+ "flex-direction": "row",
+ "justify-content": "space-between",
+ "align-items": "center",
+ "margin-top": "32px",
+ "margin-bottom": "32px",
+ })
+ .instanceName("Title Container")
+ .addChild(heading)
+}
diff --git a/packages/builder/src/builderStore/store/screenTemplates/sanitizeUrl.js b/packages/builder/src/builderStore/store/screenTemplates/utils/sanitizeUrl.js
similarity index 100%
rename from packages/builder/src/builderStore/store/screenTemplates/sanitizeUrl.js
rename to packages/builder/src/builderStore/store/screenTemplates/utils/sanitizeUrl.js
diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
index 39d5f92a92..f064ff923c 100644
--- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
+++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte
@@ -59,10 +59,13 @@
}
// Create autolink to newly created list page
- const listPage = screens.find(screen =>
+ const listScreen = screens.find(screen =>
screen.props._instanceName.endsWith("List")
)
- await store.actions.components.links.save(listPage.route, table.name)
+ await store.actions.components.links.save(
+ listScreen.routing.route,
+ table.name
+ )
// Navigate to new table
$goto(`./table/${table._id}`)
diff --git a/packages/builder/src/components/userInterface/DetailScreenSelect.svelte b/packages/builder/src/components/userInterface/DetailScreenSelect.svelte
index e0b2813c0d..c119985196 100644
--- a/packages/builder/src/components/userInterface/DetailScreenSelect.svelte
+++ b/packages/builder/src/components/userInterface/DetailScreenSelect.svelte
@@ -17,11 +17,11 @@
.filter(
screen =>
screen.props._component.endsWith("/rowdetail") ||
- screen.route.endsWith(":id")
+ screen.routing.route.endsWith(":id")
)
.map(screen => ({
name: screen.props._instanceName,
- url: screen.route,
+ url: screen.routing.route,
sort: screen.props._component,
})),
]
diff --git a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte
index 948ab37efd..3475a14eed 100644
--- a/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte
+++ b/packages/builder/src/components/userInterface/EventsEditor/StateBindingCascader.svelte
@@ -25,7 +25,9 @@
{:else}
diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/NavigateTo.svelte b/packages/builder/src/components/userInterface/EventsEditor/actions/NavigateTo.svelte
index 916f85dd0b..041237266e 100644
--- a/packages/builder/src/components/userInterface/EventsEditor/actions/NavigateTo.svelte
+++ b/packages/builder/src/components/userInterface/EventsEditor/actions/NavigateTo.svelte
@@ -10,7 +10,7 @@
diff --git a/packages/builder/src/components/userInterface/NewScreenModal.svelte b/packages/builder/src/components/userInterface/NewScreenModal.svelte
index b194ab9839..ebee601838 100644
--- a/packages/builder/src/components/userInterface/NewScreenModal.svelte
+++ b/packages/builder/src/components/userInterface/NewScreenModal.svelte
@@ -49,8 +49,8 @@
baseComponent = draftScreen.props._component
}
- if (draftScreen.route) {
- route = draftScreen.route
+ if (draftScreen.routing) {
+ route = draftScreen.routing.route
}
}
@@ -69,7 +69,8 @@
draftScreen.props._instanceName = name
draftScreen.props._component = baseComponent
- draftScreen.route = route
+ // TODO: need to fix this up correctly
+ draftScreen.routing = { route, accessLevelId: "ADMIN" }
await store.actions.screens.create(draftScreen)
if (createLink) {
@@ -88,7 +89,7 @@
const routeNameExists = route => {
return $allScreens.some(
- screen => screen.route.toLowerCase() === route.toLowerCase()
+ screen => screen.routing.route.toLowerCase() === route.toLowerCase()
)
}
diff --git a/packages/builder/src/components/userInterface/ScreenSelect.svelte b/packages/builder/src/components/userInterface/ScreenSelect.svelte
index b38116e4a1..1c7827e2dd 100644
--- a/packages/builder/src/components/userInterface/ScreenSelect.svelte
+++ b/packages/builder/src/components/userInterface/ScreenSelect.svelte
@@ -21,7 +21,7 @@
.filter(screen => !screen.props._component.endsWith("/rowdetail"))
.map(screen => ({
name: screen.props._instanceName,
- url: screen.route,
+ url: screen.routing.route,
sort: screen.props._component,
})),
]
@@ -54,7 +54,7 @@
if (idBinding) {
urls.push({
name: detailScreen.props._instanceName,
- url: detailScreen.route.replace(
+ url: detailScreen.routing.route.replace(
":id",
`{{ ${idBinding.runtimeBinding} }}`
),
diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock
index 289e9ec4b9..c1ede88884 100644
--- a/packages/builder/yarn.lock
+++ b/packages/builder/yarn.lock
@@ -1266,9 +1266,9 @@
integrity sha512-TSt8ROqK6wq+Hav7EhZL1I0GtsZhg28aJuuDSviBzG/NG9pC0eprf8roWjl59DKHOVWIUTPTeY+T+lipb9gf8w==
"@testing-library/dom@^7.0.3":
- version "7.27.0"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.27.0.tgz#b4c7393f488db6de18c6cfa619390e7d6da57a3b"
- integrity sha512-OH4lK9R0DdKdePG76cKQXORMpXLahqRLmyIau084TdN2sM1sfjxnGEiiTbJe/PDiam7sMOuf9uBXsTByH3pPhQ==
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.27.1.tgz#b760182513357e4448a8461f9565d733a88d71d0"
+ integrity sha512-AF56RoeUU8bO4DOvLyMI44H3O1LVKZQi2D/m5fNDr+iR4drfOFikTr26hT6IY7YG+l8g69FXsHERa+uThaYYQg==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
@@ -2103,9 +2103,9 @@ chalk@^4.0.0, chalk@^4.1.0:
supports-color "^7.1.0"
cheap-watch@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/cheap-watch/-/cheap-watch-1.0.2.tgz#bfa648aea6bcd15e9fe4dce4ac760ab81cd0133c"
- integrity sha512-jp82t+kZAW+ZVnuYuHZEGZqDaUg28uAyOhC915BcKBSYL55fpTyuJ56cYYXZG0JkCPQT80MjRD6q2KqebaPwCw==
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/cheap-watch/-/cheap-watch-1.0.3.tgz#3c4265718bcf8f1ae08f5e450f9f4693432e028e"
+ integrity sha512-xC5CruMhLzjPwJ5ecUxGu1uGmwJQykUhqd2QrCrYbwvsFYdRyviu6jG9+pccwDXJR/OpmOTOJ9yLFunVgQu9wg==
check-more-types@2.24.0, check-more-types@^2.24.0:
version "2.24.0"
@@ -2782,7 +2782,7 @@ date-fns@^1.27.2:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
-debug@4.2.0, debug@^4.1.0, debug@^4.1.1:
+debug@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
@@ -2804,12 +2804,19 @@ debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
ms "2.0.0"
debug@^3.1.0:
- version "3.2.6"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
- integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
+debug@^4.1.0, debug@^4.1.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
+ dependencies:
+ ms "2.1.2"
+
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -2965,9 +2972,9 @@ ecc-jsbn@~0.1.1:
safer-buffer "^2.1.0"
electron-to-chromium@^1.3.591:
- version "1.3.598"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.598.tgz#8f757018902ab6190323a8c5f6124d854893a35b"
- integrity sha512-G5Ztk23/ubLYVPxPXnB1uu105uzIPd4xB/D8ld8x1GaSC9+vU9NZL16nYZya8H77/7CCKKN7dArzJL3pBs8N7A==
+ version "1.3.601"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.601.tgz#881824eaef0b2f97c89e1abb5835fdd224997d34"
+ integrity sha512-ctRyXD9y0mZu8pgeNwBUhLP3Guyr5YuqkfLKYmpTwYx7o9JtCEJme9JVX4xBXPr5ZNvr/iBXUvHLFEVJQThATg==
elegant-spinner@^1.0.1:
version "1.0.1"
@@ -6892,9 +6899,9 @@ synchronous-promise@^2.0.13:
integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==
terser@^5.0.0:
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.4.0.tgz#9815c0839072d5c894e22c6fc508fbe9f5e7d7e8"
- integrity sha512-3dZunFLbCJis9TAF2VnX+VrQLctRUmt1p3W2kCsJuZE4ZgWqh//+1MZ62EanewrqKoUf4zIaDGZAvml4UDc0OQ==
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.0.tgz#1406fcb4d4bc517add3b22a9694284c040e33448"
+ integrity sha512-eopt1Gf7/AQyPhpygdKePTzaet31TvQxXvrf7xYUvD/d8qkCJm4SKPDzu+GHK5ZaYTn8rvttfqaZc3swK21e5g==
dependencies:
commander "^2.20.0"
source-map "~0.7.2"
@@ -7162,6 +7169,11 @@ uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+uuid@^8.3.1:
+ version "8.3.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31"
+ integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==
+
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
diff --git a/packages/client/src/old/render/screenRouter.js b/packages/client/src/old/render/screenRouter.js
index 0ae02841bf..78dc75b3c4 100644
--- a/packages/client/src/old/render/screenRouter.js
+++ b/packages/client/src/old/render/screenRouter.js
@@ -43,7 +43,7 @@ export const screenRouter = ({ screens, onScreenSelected, window }) => {
return sanitize(url)
}
- const routes = screens.map(s => makeRootedPath(s.route))
+ const routes = screens.map(s => makeRootedPath(s.routing?.route))
let fallback = routes.findIndex(([p]) => p === makeRootedPath("*"))
if (fallback < 0) fallback = 0
diff --git a/packages/server/src/api/controllers/accesslevel.js b/packages/server/src/api/controllers/accesslevel.js
index 143c633e42..b2985e2953 100644
--- a/packages/server/src/api/controllers/accesslevel.js
+++ b/packages/server/src/api/controllers/accesslevel.js
@@ -1,10 +1,9 @@
const CouchDB = require("../../db")
const {
- generateAdminPermissions,
- generatePowerUserPermissions,
- POWERUSER_LEVEL_ID,
- ADMIN_LEVEL_ID,
-} = require("../../utilities/accessLevels")
+ BUILTIN_LEVELS,
+ AccessLevel,
+ getAccessLevel,
+} = require("../../utilities/security/accessLevels")
const {
generateAccessLevelID,
getAccessLevelParams,
@@ -19,85 +18,26 @@ exports.fetch = async function(ctx) {
)
const customAccessLevels = body.rows.map(row => row.doc)
- const staticAccessLevels = [
- {
- _id: ADMIN_LEVEL_ID,
- name: "Admin",
- permissions: await generateAdminPermissions(ctx.user.appId),
- },
- {
- _id: POWERUSER_LEVEL_ID,
- name: "Power User",
- permissions: await generatePowerUserPermissions(ctx.user.appId),
- },
- ]
-
+ const staticAccessLevels = [BUILTIN_LEVELS.ADMIN, BUILTIN_LEVELS.POWER]
ctx.body = [...staticAccessLevels, ...customAccessLevels]
}
exports.find = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
- ctx.body = await db.get(ctx.params.levelId)
+ ctx.body = await getAccessLevel(ctx.user.appId, ctx.params.levelId)
}
-exports.update = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
- const level = await db.get(ctx.params.levelId)
- level.name = ctx.body.name
- level.permissions = ctx.request.body.permissions
- const result = await db.put(level)
- level._rev = result.rev
- ctx.body = level
- ctx.message = `Level ${level.name} updated successfully.`
-}
-
-exports.patch = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
- const level = await db.get(ctx.params.levelId)
- const { removedPermissions, addedPermissions, _rev } = ctx.request.body
-
- if (!_rev) throw new Error("Must supply a _rev to update an access level")
-
- level._rev = _rev
-
- if (removedPermissions) {
- level.permissions = level.permissions.filter(
- p =>
- !removedPermissions.some(
- rem => rem.name === p.name && rem.itemId === p.itemId
- )
- )
- }
-
- if (addedPermissions) {
- level.permissions = [
- ...level.permissions.filter(
- p =>
- !addedPermissions.some(
- add => add.name === p.name && add.itemId === p.itemId
- )
- ),
- ...addedPermissions,
- ]
- }
-
- const result = await db.put(level)
- level._rev = result.rev
- ctx.body = level
- ctx.message = `Access Level ${level.name} updated successfully.`
-}
-
-exports.create = async function(ctx) {
+exports.save = async function(ctx) {
const db = new CouchDB(ctx.user.appId)
- const level = {
- name: ctx.request.body.name,
- _rev: ctx.request.body._rev,
- permissions: ctx.request.body.permissions || [],
- _id: generateAccessLevelID(),
- type: "accesslevel",
+ let id = ctx.request.body._id || generateAccessLevelID()
+ const level = new AccessLevel(
+ id,
+ ctx.request.body.name,
+ ctx.request.body.inherits
+ )
+ if (ctx.request.body._rev) {
+ level._rev = ctx.request.body._rev
}
-
const result = await db.put(level)
level._rev = result.rev
ctx.body = level
diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js
index 2185293352..512299c5c8 100644
--- a/packages/server/src/api/controllers/application.js
+++ b/packages/server/src/api/controllers/application.js
@@ -8,15 +8,18 @@ const fs = require("fs-extra")
const { join, resolve } = require("../../utilities/centralPath")
const packageJson = require("../../../package.json")
const { createLinkView } = require("../../db/linkedRows")
+const { createRoutingView } = require("../../utilities/routing")
const { downloadTemplate } = require("../../utilities/templates")
const {
generateAppID,
DocumentTypes,
SEPARATOR,
getPageParams,
+ getScreenParams,
generatePageID,
generateScreenID,
} = require("../../db/utils")
+const { BUILTIN_LEVEL_IDS } = require("../../utilities/security/accessLevels")
const {
downloadExtractComponentLibraries,
} = require("../../utilities/createAppPackage")
@@ -26,6 +29,20 @@ const { cloneDeep } = require("lodash/fp")
const APP_PREFIX = DocumentTypes.APP + SEPARATOR
+// utility function, need to do away with this
+async function getMainAndUnauthPage(db) {
+ let pages = await db.allDocs(
+ getPageParams(null, {
+ include_docs: true,
+ })
+ )
+ pages = pages.rows.map(row => row.doc)
+
+ const mainPage = pages.find(page => page.name === PageTypes.MAIN)
+ const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED)
+ return { mainPage, unauthPage }
+}
+
async function createInstance(template) {
const appId = generateAppID()
@@ -38,6 +55,7 @@ async function createInstance(template) {
})
// add view for linked rows
await createLinkView(appId)
+ await createRoutingView(appId)
// replicate the template data to the instance DB
if (template) {
@@ -65,19 +83,36 @@ exports.fetch = async function(ctx) {
}
}
+exports.fetchAppDefinition = async function(ctx) {
+ const db = new CouchDB(ctx.params.appId)
+ // TODO: need to get rid of pages here, they shouldn't be needed anymore
+ const { mainPage, unauthPage } = await getMainAndUnauthPage(db)
+ const userAccessLevelId =
+ !ctx.user.accessLevel || !ctx.user.accessLevel._id
+ ? BUILTIN_LEVEL_IDS.PUBLIC
+ : ctx.user.accessLevel._id
+ const correctPage =
+ userAccessLevelId === BUILTIN_LEVEL_IDS.PUBLIC ? unauthPage : mainPage
+ const screens = (
+ await db.allDocs(
+ getScreenParams(correctPage._id, {
+ include_docs: true,
+ })
+ )
+ ).rows.map(row => row.doc)
+ // TODO: need to handle access control here, limit screens to user access level
+ ctx.body = {
+ page: correctPage,
+ screens: screens,
+ libraries: ["@budibase/standard-components"],
+ }
+}
+
exports.fetchAppPackage = async function(ctx) {
const db = new CouchDB(ctx.params.appId)
const application = await db.get(ctx.params.appId)
- let pages = await db.allDocs(
- getPageParams(null, {
- include_docs: true,
- })
- )
- pages = pages.rows.map(row => row.doc)
-
- const mainPage = pages.find(page => page.name === PageTypes.MAIN)
- const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED)
+ const { mainPage, unauthPage } = await getMainAndUnauthPage(db)
ctx.body = {
application,
pages: {
diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js
index 2c162587b3..21136b0214 100644
--- a/packages/server/src/api/controllers/auth.js
+++ b/packages/server/src/api/controllers/auth.js
@@ -34,6 +34,7 @@ exports.authenticate = async ctx => {
userId: dbUser._id,
accessLevelId: dbUser.accessLevelId,
version: app.version,
+ permissions: dbUser.permissions || [],
}
// if in cloud add the user api key
if (env.CLOUD) {
diff --git a/packages/server/src/api/controllers/routing.js b/packages/server/src/api/controllers/routing.js
new file mode 100644
index 0000000000..8d96863593
--- /dev/null
+++ b/packages/server/src/api/controllers/routing.js
@@ -0,0 +1,101 @@
+const { getRoutingInfo } = require("../../utilities/routing")
+const {
+ getUserAccessLevelHierarchy,
+ BUILTIN_LEVEL_IDS,
+} = require("../../utilities/security/accessLevels")
+
+const URL_SEPARATOR = "/"
+
+function Routing() {
+ this.json = {}
+}
+
+Routing.prototype.getTopLevel = function(fullpath) {
+ if (fullpath.charAt(0) !== URL_SEPARATOR) {
+ fullpath = URL_SEPARATOR + fullpath
+ }
+ // replace the first value with the home route
+ return URL_SEPARATOR + fullpath.split(URL_SEPARATOR)[1]
+}
+
+Routing.prototype.getScreensProp = function(fullpath) {
+ const topLevel = this.getTopLevel(fullpath)
+ if (!this.json[topLevel]) {
+ this.json[topLevel] = {
+ subpaths: {},
+ }
+ }
+ if (!this.json[topLevel].subpaths[fullpath]) {
+ this.json[topLevel].subpaths[fullpath] = {
+ screens: {},
+ }
+ }
+ return this.json[topLevel].subpaths[fullpath].screens
+}
+
+Routing.prototype.addScreenId = function(fullpath, accessLevel, screenId) {
+ this.getScreensProp(fullpath)[accessLevel] = screenId
+}
+
+/**
+ * Gets the full routing structure by querying the routing view and processing the result into the tree.
+ * @param {string} appId The application to produce the routing structure for.
+ * @returns {Promise