diff --git a/packages/builder/cypress/integration/screens.spec.js b/packages/builder/cypress/integration/screens.spec.js index f78d7266e8..6df8af3602 100644 --- a/packages/builder/cypress/integration/screens.spec.js +++ b/packages/builder/cypress/integration/screens.spec.js @@ -7,17 +7,7 @@ context('Screen Tests', () => { cy.navigateToFrontend() }) - it('Should successful create a screen', () => { + it('Should successfully create a screen', () => { cy.createScreen("test Screen", "/test") }) - - it('Should rename a screen', () => { - cy.get(".components-pane").within(() => { - cy.contains("Settings").click() - cy.get("input[name=_instanceName]").clear().type("About Us").blur() - }) - cy.get('.nav-items-container').within(() => { - cy.contains("About Us").should('exist') - }) - }) }) \ No newline at end of file diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index f82ff30827..15564083ba 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -166,6 +166,6 @@ Cypress.Commands.add("createScreen", (screenName, route) => { cy.contains("Create Screen").click() }) cy.get(".nav-items-container").within(() => { - cy.contains(screenName).should("exist") + cy.contains(route).should("exist") }) }) diff --git a/packages/builder/package.json b/packages/builder/package.json index 68ff7f79d0..4254e3c7f6 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/index.js b/packages/builder/src/builderStore/index.js index 1bd86480a4..ae77889404 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -31,6 +31,12 @@ export const currentScreens = derived(store, $store => { : Object.values(currentScreens) }) +export const selectedPage = derived(store, $store => { + if (!$store.pages) return null + + return $store.pages[$store.currentPageName || "main"] +}) + export const initialise = async () => { try { await analytics.activate() diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 31b8dcff22..778c7f7be5 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -5,8 +5,7 @@ import { getBuiltin, makePropsSafe, } from "components/userInterface/pagesParsing/createProps" -import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents" -import { allScreens, backendUiStore } from "builderStore" +import { allScreens, backendUiStore, selectedPage } from "builderStore" import { generate_screen_css } from "../generate_css" import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import api from "../api" @@ -37,6 +36,7 @@ const INITIAL_FRONTEND_STATE = { hasAppPackage: false, libraries: null, appId: "", + routes: {}, } export const getFrontendStore = () => { @@ -111,10 +111,9 @@ export const getFrontendStore = () => { store.update(state => { state.currentFrontEndType = type - const pageOrScreen = - type === "page" - ? state.pages[state.currentPageName] - : state.pages[state.currentPageName]._screens[0] + const page = get(selectedPage) + + const pageOrScreen = type === "page" ? page : page._screens[0] state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null state.currentPreviewItem = pageOrScreen @@ -122,10 +121,21 @@ export const getFrontendStore = () => { return state }) }, - screens: { - select: screenName => { + routing: { + fetch: async () => { + const response = await api.get("/api/routing") + const json = await response.json() + store.update(state => { - const screen = getExactComponent(get(allScreens), screenName, true) + state.routes = json.routes + return state + }) + }, + }, + screens: { + select: screenId => { + store.update(state => { + const screen = get(allScreens).find(screen => screen._id === screenId) state.currentPreviewItem = screen state.currentFrontEndType = "screen" state.currentView = "detail" @@ -158,32 +168,25 @@ export const getFrontendStore = () => { await savePromise }, save: async screen => { - const storeContents = get(store) - const pageName = storeContents.currentPageName || "main" - const currentPage = storeContents.pages[pageName] - const currentPageScreens = currentPage._screens + const page = get(selectedPage) + const currentPageScreens = page._screens const creatingNewScreen = screen._id === undefined let savePromise - const response = await api.post( - `/api/screens/${currentPage._id}`, - screen - ) + const response = await api.post(`/api/screens/${page._id}`, screen) const json = await response.json() screen._rev = json.rev screen._id = json.id - const foundScreen = currentPageScreens.findIndex( - el => el._id === screen._id - ) + const foundScreen = page._screens.findIndex(el => el._id === screen._id) if (foundScreen !== -1) { - currentPageScreens.splice(foundScreen, 1) + page._screens.splice(foundScreen, 1) } - currentPageScreens.push(screen) + page._screens.push(screen) // TODO: should carry out all server updates to screen in a single call store.update(state => { - state.pages[pageName]._screens = currentPageScreens + page._screens = currentPageScreens if (creatingNewScreen) { state.currentPreviewItem = screen @@ -209,21 +212,21 @@ export const getFrontendStore = () => { store.actions.screens.regenerateCss(currentPreviewItem) } }, - delete: async (screensToDelete, pageName) => { + delete: async screens => { let deletePromise + const screensToDelete = Array.isArray(screens) ? screens : [screens] + store.update(state => { - if (pageName == null) { - pageName = state.pages.main.name - } - for (let screenToDelete of Array.isArray(screensToDelete) - ? screensToDelete - : [screensToDelete]) { + const currentPage = get(selectedPage) + + for (let screenToDelete of screensToDelete) { // Remove screen from current page as well // TODO: Should be done server side - state.pages[pageName]._screens = state.pages[ - pageName - ]._screens.filter(scr => scr._id !== screenToDelete._id) + currentPage._screens = currentPage._screens.filter( + scr => scr._id !== screenToDelete._id + ) + deletePromise = api.delete( `/api/screens/${screenToDelete._id}/${screenToDelete._rev}` ) @@ -309,14 +312,13 @@ export const getFrontendStore = () => { create: (componentToAdd, presetProps) => { store.update(state => { function findSlot(component_array) { - for (let i = 0; i < component_array.length; i += 1) { - if (component_array[i]._component === "##builtin/screenslot") { + for (let component of component_array) { + if (component._component === "##builtin/screenslot") { return true } - if (component_array[i]._children) findSlot(component_array[i]) + if (component._children) findSlot(component) } - return false } @@ -377,7 +379,7 @@ export const getFrontendStore = () => { component._id ) parent._children = parent._children.filter( - c => c._id !== component._id + child => child._id !== component._id ) store.actions.components.select(parent) } 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..0a40c62dc7 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Component.js @@ -0,0 +1,51 @@ +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..523296e959 --- /dev/null +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js @@ -0,0 +1,61 @@ +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", + } + } + + normalStyle(styling) { + this._json.props._styles.normal = styling + return this + } + + 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/settings/tabs/DangerZone.svelte b/packages/builder/src/components/settings/tabs/DangerZone.svelte index ee592ec6b1..8c7f71e7fd 100644 --- a/packages/builder/src/components/settings/tabs/DangerZone.svelte +++ b/packages/builder/src/components/settings/tabs/DangerZone.svelte @@ -9,7 +9,7 @@ async function deleteApp() { loading = true const id = $params.application - await del(`/api/${id}`) + await del(`/api/applications/${id}`) loading = false $goto("/") } diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index c8a894d9d9..049b21c995 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -149,7 +149,9 @@ }) // Select Correct Application/DB in prep for creating user - const applicationPkg = await get(`/api/${appJson._id}/appPackage`) + const applicationPkg = await get( + `/api/applications/${appJson._id}/appPackage` + ) const pkg = await applicationPkg.json() if (applicationPkg.ok) { backendUiStore.actions.reset() diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index fd7270cdfa..b3ec41b2a2 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -3,6 +3,8 @@ import { map, join } from "lodash/fp" import iframeTemplate from "./iframeTemplate" import { pipe } from "../../../helpers" + import { Screen } from "../../../builderStore/store/screenTemplates/utils/Screen" + import { Component } from "../../../builderStore/store/screenTemplates/utils/Component" let iframe let styles = "" @@ -21,114 +23,52 @@ return componentName || "element" } - const screenPlaceholder = { - name: "Screen Placeholder", - route: "*", - props: { - _id: "screenslot-placeholder", - _component: "@budibase/standard-components/container", - _styles: { - normal: { - flex: "1 1 auto", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - className: "", - onLoad: [], - type: "div", - _children: [ - { - _id: "51a1b494-0fa4-49c3-90cc-c2a6c7a3f888", - _component: "@budibase/standard-components/container", - _styles: { - normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - flex: "1 1 auto", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - className: "", - onLoad: [], - type: "div", - _instanceId: "inst_40d9036_4c81114e2bf145ab8721978c66e09a10", - _instanceName: "Container", - _children: [ - { - _id: "90a52cd0-f215-46c1-b29b-e28f9e7edf72", - _component: "@budibase/standard-components/heading", - _styles: { - normal: { - width: "500px", - padding: "8px", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - className: "", - text: "Screen Slot", - type: "h1", - _instanceId: "inst_40d9036_4c81114e2bf145ab8721978c66e09a10", - _instanceName: "Heading", - _children: [], - }, - { - _id: "71a3da65-72c6-4c43-8c6a-49871c07b77d", - _component: "@budibase/standard-components/text", - _styles: { - normal: { - "max-width": "", - "text-align": "left", - width: "500px", - padding: "8px", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - text: - "The screens that you create will be displayed inside this box.", - type: "none", - _instanceId: "inst_40d9036_4c81114e2bf145ab8721978c66e09a10", - _instanceName: "Text", - }, - { - _id: "8af80374-460d-497b-a5d8-7dd2ec4a7bbc", - _component: "@budibase/standard-components/text", - _styles: { - normal: { - "max-width": "", - "text-align": "left", - width: "500px", - padding: "8px", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - text: - "This box is just a placeholder, to show you the position of screens.", - type: "none", - _instanceId: "inst_40d9036_4c81114e2bf145ab8721978c66e09a10", - _instanceName: "Text", - }, - ], - }, - ], - _instanceName: "Content Placeholder", - }, + const headingStyle = { + width: "500px", + padding: "8px", } + const textStyle = { + ...headingStyle, + "max-width": "", + "text-align": "left", + } + + const heading = new Component("@budibase/standard-components/heading") + .normalStyle(headingStyle) + .type("h1") + .text("Screen Slot") + .instanceName("Heading") + const textScreenDisplay = new Component("@budibase/standard-components/text") + .normalStyle(textStyle) + .instanceName("Text") + .type("none") + .text( + "The screens that you create will be displayed inside this box. This box is just a placeholder, to show you the position of screens." + ) + const container = new Component("@budibase/standard-components/container") + .normalStyle({ + display: "flex", + "flex-direction": "column", + "align-items": "center", + flex: "1 1 auto", + }) + .type("div") + .instanceName("Container") + .addChild(heading) + .addChild(textScreenDisplay) + const screenPlaceholder = new Screen() + .name("Screen Placeholder") + .route("*") + .component("@budibase/standard-components/container") + .mainType("div") + .instanceName("Content Placeholder") + .normalStyle({ + flex: "1 1 auto", + }) + .addChild(container) + .json() + // TODO: this ID is attached to how the screen slot is rendered, confusing, would be better a type etc + screenPlaceholder.props._id = "screenslot-placeholder" $: hasComponent = !!$store.currentPreviewItem diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte new file mode 100644 index 0000000000..ee162f0eba --- /dev/null +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/ComponentTree.svelte @@ -0,0 +1,113 @@ + + +