feature parity with current pages, screens and store setup. Starting main bb store refactor

This commit is contained in:
Martin McKeaveney 2020-11-04 16:13:50 +00:00
parent 6bc184f0d6
commit 333844a1f0
18 changed files with 725 additions and 302 deletions

View File

@ -1,10 +1,12 @@
import { getStore } from "./store" import { getStore } from "./store"
// import { getFrontendStore } from "./store/frontend"
import { getBackendUiStore } from "./store/backend" import { getBackendUiStore } from "./store/backend"
import { getAutomationStore } from "./store/automation/" import { getAutomationStore } from "./store/automation/"
import { getThemeStore } from "./store/theme" import { getThemeStore } from "./store/theme"
import analytics from "analytics" import analytics from "analytics"
export const store = getStore() export const store = getStore()
// export const store = getFrontendStore()
export const backendUiStore = getBackendUiStore() export const backendUiStore = getBackendUiStore()
export const automationStore = getAutomationStore() export const automationStore = getAutomationStore()
export const themeStore = getThemeStore() export const themeStore = getThemeStore()

View File

@ -0,0 +1,483 @@
import { writable, get } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import {
createProps,
makePropsSafe,
getBuiltin,
} from "components/userInterface/pagesParsing/createProps"
import { getExactComponent } from "components/userInterface/pagesParsing/searchComponents"
import { backendUiStore } from "builderStore"
import { generate_screen_css } from "../generate_css"
import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api"
import { DEFAULT_PAGES_OBJECT } from "../../constants"
import getNewComponentName from "../getNewComponentName"
import analytics from "analytics"
import {
getParent,
// saveScreenApi as _saveScreenApi,
generateNewIdsForComponent,
getComponentDefinition,
} from "../storeUtils"
const INITIAL_FRONTEND_STATE = {
apps: [],
name: "",
description: "",
pages: DEFAULT_PAGES_OBJECT,
mainUi: {},
unauthenticatedUi: {},
components: [],
currentPreviewItem: null,
currentComponentInfo: null,
currentFrontEndType: "none",
currentPageName: "",
currentComponentProps: null,
errors: [],
hasAppPackage: false,
libraries: null,
appId: "",
}
export const getFrontendStore = () => {
const store = writable({ ...INITIAL_FRONTEND_STATE })
store.actions = {
// TODO: REFACTOR
initialise: async pkg => {
const screens = await api.get("/api/screens").then(r => r.json())
const mainScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.main._id)
),
unauthScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.unauthenticated._id)
)
pkg.pages = {
main: {
...pkg.pages.main,
_screens: mainScreens,
},
unauthenticated: {
...pkg.pages.unauthenticated,
_screens: unauthScreens,
},
}
// if the app has just been created
// we need to build the CSS and save
if (pkg.justCreated) {
for (let pageName of ["main", "unauthenticated"]) {
const page = pkg.pages[pageName]
store.actions.screens.regenerateCss(page)
for (let screen of page._screens) {
store.actions.screens.regenerateCss(screen)
}
await api.post(`/api/pages/${page._id}`, {
page: {
componentLibraries: pkg.application.componentLibraries,
...page,
},
screens: page._screens,
})
}
pkg.justCreated = false
const components = await fetchComponentLibDefinitions(
pkg.application._id
)
store.update(state => ({
...state,
libraries: pkg.application.componentLibraries,
components,
name: pkg.application.name,
description: pkg.application.description,
appId: pkg.application._id,
pages: pkg.pages,
hasAppPackage: true,
screens: [
...Object.values(mainScreens),
...Object.values(unauthScreens),
],
builtins: [getBuiltin("##builtin/screenslot")],
appInstance: pkg.application.instance,
}))
await backendUiStore.actions.database.select(pkg.application.instance)
}
},
// store.setScreenType
selectPageOrScreen: type => {
store.update(state => {
state.currentFrontEndType = type
const pageOrScreen =
type === "page"
? state.pages[state.currentPageName]
: state.pages[state.currentPageName]._screens[0]
state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null
state.currentPreviewItem = pageOrScreen
state.currentView = "detail"
return state
})
},
screens: {
select: screenName => {
store.update(state => {
const screen = getExactComponent(state.screens, screenName, true)
state.currentPreviewItem = screen
state.currentFrontEndType = "screen"
state.currentView = "detail"
store.actions.screens.regenerateCssForCurrentScreen()
// this.regenerateCssForCurrentScreen()
// regenerateCssForCurrentScreen(s)
const safeProps = makePropsSafe(
state.components[screen.props._component],
screen.props
)
screen.props = safeProps
state.currentComponentInfo = safeProps
return state
})
},
create: async screen => {
let savePromise
store.update(state => {
state.currentPreviewItem = screen
state.currentComponentInfo = screen.props
state.currentFrontEndType = "screen"
if (state.currentPreviewItem) {
store.actions.screens.regenerateCss(state.currentPreviewItem)
}
savePromise = store.actions.screens.save(screen)
return state
})
await savePromise
},
save: async screen => {
const storeContents = get(store)
const pageName = storeContents.currentPageName || "main"
const currentPage = storeContents.pages[pageName]
const currentPageScreens = currentPage._screens
let savePromise
const response = await api.post(
`/api/screens/${currentPage._id}`,
screen
)
const json = await response.json()
if (currentPageScreens.includes(screen)) return
screen._rev = json.rev
screen._id = json.id
const screens = [...currentPageScreens, screen]
// TODO: should carry out all server updates to screen in a single call
store.update(state => {
state.pages[pageName]._screens = screens
state.screens = screens
state.currentPreviewItem = screen
const safeProps = makePropsSafe(
state.components[screen.props._component],
screen.props
)
state.currentComponentInfo = safeProps
screen.props = safeProps
savePromise = store.actions.pages.save()
return state
})
await savePromise
},
regenerateCss: screen => {
screen._css = generate_screen_css([screen.props])
},
regenerateCssForCurrentScreen: () => {
const { currentPreviewItem } = get(store)
if (currentPreviewItem) {
store.actions.screens.regenerateCss(currentPreviewItem)
}
},
delete: async (screensToDelete, pageName) => {
let deletePromise
store.update(state => {
if (pageName == null) {
pageName = state.pages.main.name
}
for (let screenToDelete of Array.isArray(screenToDelete)
? screenToDelete
: [screenToDelete]) {
state.screens = state.screens.filter(
screen => screen.name !== screenToDelete.name
)
// Remove screen from current page as well
// TODO: Should be done server side
state.pages[pageName]._screens = state.pages[
pageName
]._screens.filter(scr => scr.name !== screenToDelete.name)
deletePromise = api.delete(
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
)
}
return state
})
await deletePromise
},
},
preview: {
// _saveCurrentPreviewItem
saveSelected: () => {
const state = get(store)
state.currentFrontEndType === "page"
? store.actions.pages.save()
: store.actions.screens.save(state.currentPreviewItem)
},
pages: {
select: pageName => {
store.update(state => {
const current_screens = state.pages[pageName]._screens
const currentPage = state.pages[pageName]
state.currentFrontEndType = "page"
state.currentView = "detail"
state.currentPageName = pageName
state.screens = Array.isArray(current_screens)
? current_screens
: Object.values(current_screens)
const safeProps = makePropsSafe(
state.components[currentPage.props._component],
currentPage.props
)
state.currentComponentInfo = safeProps
currentPage.props = safeProps
state.currentPreviewItem = state.pages[pageName]
store.actions.screens.regenerateCssForCurrentScreen()
for (let screen of state.screens) {
screen._css = generate_screen_css([screen.props])
}
return state
})
},
save: async page => {
const storeContents = get(store)
const pageName = storeContents.currentPageName || "main"
const pageToSave = page || storeContents.pages[pageName]
// TODO: revisit. This sends down a very weird payload
const response = await api
.post(`/api/pages/${pageToSave._id}`, {
page: {
componentLibraries: storeContents.pages.componentLibraries,
...pageToSave,
},
screens: pageToSave._screens,
})
.then(response => response.json())
store.update(state => {
state.pages[pageName]._rev = response.rev
return state
})
},
},
components: {
select: component => {
store.update(state => {
const componentDef = component._component.startsWith("##")
? component
: state.components[component._component]
state.currentComponentInfo = makePropsSafe(componentDef, component)
state.currentView = "component"
return state
})
},
// addChildComponent
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") {
return true
}
if (component_array[i]._children) findSlot(component_array[i])
}
return false
}
if (
componentToAdd.startsWith("##") &&
findSlot(state.pages[state.currentPageName].props._children)
) {
return state
}
const component = getComponentDefinition(state, componentToAdd)
const instanceId = get(backendUiStore).selectedDatabase._id
const instanceName = getNewComponentName(component, state)
const newComponent = createProps(
component,
{
...presetProps,
_instanceId: instanceId,
_instanceName: instanceName,
},
state
)
const currentComponent =
state.components[state.currentComponentInfo._component]
const targetParent = currentComponent.children
? state.currentComponentInfo
: getParent(
state.currentPreviewItem.props,
state.currentComponentInfo
)
// Don't continue if there's no parent
if (!targetParent) {
return state
}
targetParent._children = targetParent._children.concat(
newComponent.props
)
store.actions.preview.saveSelected()
state.currentView = "component"
state.currentComponentInfo = newComponent.props
analytics.captureEvent("Added Component", {
name: newComponent.props._component,
})
return state
})
},
copy: (component, cut = false) => {
store.update(state => {
const copiedComponent = cloneDeep(component)
state.componentToPaste = copiedComponent
state.componentToPaste.isCut = cut
if (cut) {
const parent = getParent(
state.currentPreviewItem.props,
component._id
)
parent._children = parent._children.filter(
c => c._id !== component._id
)
store.actions.components.select(parent)
}
return state
})
},
paste: (targetComponent, mode) => {
store.update(state => {
if (!state.componentToPaste) return state
const componentToPaste = cloneDeep(state.componentToPaste)
// retain the same ids as things may be referencing this component
if (componentToPaste.isCut) {
// in case we paste a second time
state.componentToPaste.isCut = false
} else {
generateNewIdsForComponent(componentToPaste, state)
}
delete componentToPaste.isCut
if (mode === "inside") {
targetComponent._children.push(componentToPaste)
return state
}
const parent = getParent(
state.currentPreviewItem.props,
targetComponent
)
const targetIndex = parent._children.indexOf(targetComponent)
const index = mode === "above" ? targetIndex : targetIndex + 1
parent._children.splice(index, 0, cloneDeep(componentToPaste))
store.actions.screens.regenerateCssForCurrentScreen()
store.actions.preview.saveSelected()
store.actions.components.select(componentToPaste)
return state
})
},
updateStyle: (type, name, value) => {
store.update(state => {
if (!state.currentComponentInfo._styles) {
state.currentComponentInfo._styles = {}
}
state.currentComponentInfo._styles[type][name] = value
store.actions.screens.regenerateCssForCurrentScreen()
// save without messing with the store
store.actions.preview.saveSelected()
return state
})
},
updateProp: (name, value) => {
store.update(state => {
let current_component = state.currentComponentInfo
current_component[name] = value
state.currentComponentInfo = current_component
store.actions.preview.saveSelected()
return state
})
},
findRoute: component => {
// Gets all the components to needed to construct a path.
const tempStore = get(store)
let pathComponents = []
let parent = component
let root = false
while (!root) {
parent = getParent(tempStore.currentPreviewItem.props, parent)
if (!parent) {
root = true
} else {
pathComponents.push(parent)
}
}
// Remove root entry since it's the screen or page layout.
// Reverse array since we need the correct order of the IDs
const reversedComponents = pathComponents.reverse().slice(1)
// Add component
const allComponents = [...reversedComponents, component]
// Map IDs
const IdList = allComponents.map(c => c._id)
// Construct ID Path:
const path = IdList.join("/")
return path
},
},
},
}
}

View File

@ -20,7 +20,7 @@ import {
walkProps, walkProps,
savePage as _savePage, savePage as _savePage,
saveCurrentPreviewItem as _saveCurrentPreviewItem, saveCurrentPreviewItem as _saveCurrentPreviewItem,
saveScreenApi as _saveScreenApi, // saveScreenApi as _saveScreenApi,
regenerateCssForCurrentScreen, regenerateCssForCurrentScreen,
regenerateCssForScreen, regenerateCssForScreen,
generateNewIdsForComponent, generateNewIdsForComponent,
@ -58,7 +58,7 @@ export const getStore = () => {
store.setCurrentPage = setCurrentPage(store) store.setCurrentPage = setCurrentPage(store)
store.createLink = createLink(store) store.createLink = createLink(store)
store.createScreen = createScreen(store) store.createScreen = createScreen(store)
store.savePage = savePage(store) // store.savePage = savePage(store)
store.addChildComponent = addChildComponent(store) store.addChildComponent = addChildComponent(store)
store.selectComponent = selectComponent(store) store.selectComponent = selectComponent(store)
store.setComponentProp = setComponentProp(store) store.setComponentProp = setComponentProp(store)
@ -66,9 +66,6 @@ export const getStore = () => {
store.setComponentStyle = setComponentStyle(store) store.setComponentStyle = setComponentStyle(store)
store.setScreenType = setScreenType(store) store.setScreenType = setScreenType(store)
store.getPathToComponent = getPathToComponent(store) store.getPathToComponent = getPathToComponent(store)
store.addTemplatedComponent = addTemplatedComponent(store)
store.setMetadataProp = setMetadataProp(store)
store.editPageOrScreen = editPageOrScreen(store)
store.pasteComponent = pasteComponent(store) store.pasteComponent = pasteComponent(store)
store.storeComponentForCopy = storeComponentForCopy(store) store.storeComponentForCopy = storeComponentForCopy(store)
return store return store
@ -79,8 +76,12 @@ export default getStore
const setPackage = (store, initial) => async pkg => { const setPackage = (store, initial) => async pkg => {
const screens = await api.get("/api/screens").then(r => r.json()) const screens = await api.get("/api/screens").then(r => r.json())
const mainScreens = screens.filter(screen => screen._id.includes(pkg.pages.main._id)), const mainScreens = screens.filter(screen =>
unauthScreens = screens.filter(screen => screen._id.includes(pkg.pages.unauthenticated._id)) screen._id.includes(pkg.pages.main._id)
),
unauthScreens = screens.filter(screen =>
screen._id.includes(pkg.pages.unauthenticated._id)
)
pkg.pages = { pkg.pages = {
main: { main: {
...pkg.pages.main, ...pkg.pages.main,
@ -141,11 +142,14 @@ const saveScreen = store => async screen => {
const currentPageScreens = currentPage._screens const currentPageScreens = currentPage._screens
let savePromise let savePromise
await api const response = await api.post(`/api/screens/${currentPage._id}`, screen)
.post(`/api/screens/${currentPage._id}`, screen) const json = await response.json()
.then(() => {
if (currentPageScreens.includes(screen)) return if (currentPageScreens.includes(screen)) return
screen._rev = json.rev
screen._id = json.id
const screens = [...currentPageScreens, screen] const screens = [...currentPageScreens, screen]
// TODO: should carry out all server updates to screen in a single call // TODO: should carry out all server updates to screen in a single call
@ -162,7 +166,6 @@ const saveScreen = store => async screen => {
savePromise = _savePage(state) savePromise = _savePage(state)
return state return state
}) })
})
await savePromise await savePromise
} }
@ -268,17 +271,17 @@ const deleteScreens = store => (screens, pageName = null) => {
}) })
} }
const savePage = store => async page => { // const savePage = store => async page => {
store.update(state => { // store.update(state => {
if (state.currentFrontEndType !== "page" || !state.currentPageName) { // if (state.currentFrontEndType !== "page" || !state.currentPageName) {
return state // return state
} // }
state.pages[state.currentPageName] = page // state.pages[state.currentPageName] = page
_savePage(state) // _savePage(state)
return state // return state
}) // })
} // }
const setCurrentPage = store => pageName => { const setCurrentPage = store => pageName => {
store.update(state => { store.update(state => {
@ -365,7 +368,7 @@ const addChildComponent = store => (componentToAdd, presetProps = {}) => {
state.currentFrontEndType === "page" state.currentFrontEndType === "page"
? _savePage(state) ? _savePage(state)
: _saveScreenApi(state.currentPreviewItem, state) : saveScreen(state.currentPreviewItem)
state.currentView = "component" state.currentView = "component"
state.currentComponentInfo = newComponent.props state.currentComponentInfo = newComponent.props
@ -376,26 +379,6 @@ const addChildComponent = store => (componentToAdd, presetProps = {}) => {
}) })
} }
/**
* @param {string} props - props to add, as child of current component
*/
const addTemplatedComponent = store => props => {
store.update(state => {
walkProps(props, p => {
p._id = uuid()
})
state.currentComponentInfo._children = state.currentComponentInfo._children.concat(
props
)
regenerateCssForCurrentScreen(state)
_saveCurrentPreviewItem(state)
return state
})
}
const selectComponent = store => component => { const selectComponent = store => component => {
store.update(state => { store.update(state => {
return _selectComponent(state, component) return _selectComponent(state, component)
@ -440,6 +423,7 @@ const setComponentStyle = store => (type, name, value) => {
}) })
} }
// Select page or screen
const setScreenType = store => type => { const setScreenType = store => type => {
store.update(state => { store.update(state => {
state.currentFrontEndType = type state.currentFrontEndType = type
@ -456,17 +440,6 @@ const setScreenType = store => type => {
}) })
} }
const editPageOrScreen = store => (key, value, setOnComponent = false) => {
store.update(state => {
setOnComponent
? (state.currentPreviewItem.props[key] = value)
: (state.currentPreviewItem[key] = value)
_saveCurrentPreviewItem(state)
return state
})
}
const getPathToComponent = store => component => { const getPathToComponent = store => component => {
// Gets all the components to needed to construct a path. // Gets all the components to needed to construct a path.
const tempStore = get(store) const tempStore = get(store)
@ -498,13 +471,6 @@ const getPathToComponent = store => component => {
return path return path
} }
const setMetadataProp = store => (name, prop) => {
store.update(s => {
s.currentPreviewItem[name] = prop
return s
})
}
const storeComponentForCopy = store => (component, cut = false) => { const storeComponentForCopy = store => (component, cut = false) => {
store.update(s => { store.update(s => {
const copiedComponent = cloneDeep(component) const copiedComponent = cloneDeep(component)

View File

@ -34,17 +34,18 @@ export const getParent = (rootProps, child) => {
export const saveCurrentPreviewItem = s => export const saveCurrentPreviewItem = s =>
s.currentFrontEndType === "page" s.currentFrontEndType === "page"
? savePage(s) ? savePage(s)
: saveScreenApi(s.currentPreviewItem, s) : store.saveScreen(s.currentPreviewItem)
export const savePage = async state => { export const savePage = async state => {
const pageName = state.currentPageName || "main" const pageName = state.currentPageName || "main"
const page = state.pages[pageName] const page = state.pages[pageName]
const response = await api.post(`/api/pages/${page._id}`, { const response = await api
.post(`/api/pages/${page._id}`, {
page: { componentLibraries: state.pages.componentLibraries, ...page }, page: { componentLibraries: state.pages.componentLibraries, ...page },
uiFunctions: state.currentPageFunctions,
screens: page._screens, screens: page._screens,
}).then(response => response.json()) })
.then(response => response.json())
store.update(innerState => { store.update(innerState => {
innerState.pages[pageName]._rev = response.rev innerState.pages[pageName]._rev = response.rev
return innerState return innerState
@ -52,25 +53,19 @@ export const savePage = async state => {
return state return state
} }
export const saveScreenApi = (screen, s) => { // export const saveScreenApi = async (screen, state) => {
api // const currentPage = state.pages[state.currentPageName]
.post(`/_builder/api/${s.appId}/pages/${s.currentPageName}/screen`, screen) // const response = await api.post(`/api/screens/${currentPage._id}`, screen)
.then(() => savePage(s)) // const json = await response.json()
}
export const renameCurrentScreen = (newname, state) => { // store.update(innerState => {
const oldname = state.currentPreviewItem.props._instanceName // // TODO: need to update pages in here
state.currentPreviewItem.props._instanceName = newname // // innerState.pages[pageName]._rev = response.rev
// return innerState
// })
api.patch( // await savePage(state)
`/_builder/api/${state.appId}/pages/${state.currentPageName}/screen`, // }
{
oldname,
newname,
}
)
return state
}
export const walkProps = (props, action, cancelToken = null) => { export const walkProps = (props, action, cancelToken = null) => {
cancelToken = cancelToken || { cancelled: false } cancelToken = cancelToken || { cancelled: false }

View File

@ -1,6 +1,7 @@
<script> <script>
import { goto } from "@sveltech/routify" import { goto } from "@sveltech/routify"
import { store } from "builderStore" import { store } from "builderStore"
import { notifier } from "builderStore/store/notifications"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import { DropdownMenu } from "@budibase/bbui" import { DropdownMenu } from "@budibase/bbui"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
@ -17,6 +18,7 @@
store.update(state => { store.update(state => {
if (state.currentPreviewItem.name === screen.name) { if (state.currentPreviewItem.name === screen.name) {
store.setCurrentPage($store.currentPageName) store.setCurrentPage($store.currentPageName)
notifier.success(`Screen ${screen.name} deleted successfully.`)
$goto(`./:page/page-layout`) $goto(`./:page/page-layout`)
} }
return state return state

View File

@ -29,9 +29,10 @@ export const searchAllComponents = (components, phrase) => {
} }
export const getExactComponent = (components, name, isScreen = false) => { export const getExactComponent = (components, name, isScreen = false) => {
return components.find(c => return components.find(comp => {
isScreen ? c.props._instanceName === name : c._instanceName === name const { props, _instanceName } = comp
) return name === isScreen ? props._instanceName : _instanceName
})
} }
export const getAncestorProps = (components, name, found = []) => { export const getAncestorProps = (components, name, found = []) => {

View File

@ -223,7 +223,9 @@ const createEmptyAppPackage = async (ctx, app) => {
mainPage.title = app.name mainPage.title = app.name
const unauthPage = cloneDeep(UNAUTHENTICATED) const unauthPage = cloneDeep(UNAUTHENTICATED)
unauthPage._id = generatePageID() unauthPage._id = generatePageID()
// TODO: fix - handlebars etc
unauthPage.title = app.name unauthPage.title = app.name
unauthPage.props._children[0]._children.title = `Log in to ${app.name}`
const homeScreen = cloneDeep(HOME_SCREEN) const homeScreen = cloneDeep(HOME_SCREEN)
homeScreen._id = generateScreenID(mainPage._id) homeScreen._id = generateScreenID(mainPage._id)
await db.bulkDocs([mainPage, unauthPage, homeScreen]) await db.bulkDocs([mainPage, unauthPage, homeScreen])

View File

@ -1,8 +1,3 @@
/**
* This controller is not currently fully implemented. Screens are
* currently managed as part of the pages API, please look in api/routes/page.js
* for routes and controllers.
*/
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { getScreenParams, generateScreenID } = require("../../db/utils") const { getScreenParams, generateScreenID } = require("../../db/utils")
@ -49,4 +44,5 @@ exports.destroy = async ctx => {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
await db.remove(ctx.params.screenId, ctx.params.revId) await db.remove(ctx.params.screenId, ctx.params.revId)
ctx.message = "Screen deleted successfully" ctx.message = "Screen deleted successfully"
ctx.status = 200
} }

View File

@ -1,5 +1,5 @@
const authRoutes = require("./auth") const authRoutes = require("./auth")
const pageRoutes = require("./pages.new") const pageRoutes = require("./pages")
const screenRoutes = require("./screen") const screenRoutes = require("./screen")
const userRoutes = require("./user") const userRoutes = require("./user")
const applicationRoutes = require("./application") const applicationRoutes = require("./application")

View File

@ -1,108 +1,10 @@
const Router = require("@koa/router") const Router = require("@koa/router")
const StatusCodes = require("../../utilities/statusCodes")
const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi")
const {
listScreens,
saveScreen,
buildPage,
renameScreen,
deleteScreen,
} = require("../../utilities/builder")
const authorized = require("../../middleware/authorized") const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels") const { BUILDER } = require("../../utilities/accessLevels")
const controller = require("../controllers/page")
const router = Router() const router = Router()
function generateSaveValidation() { router.post("/api/pages/:pageId", authorized(BUILDER), controller.save)
// prettier-ignore
return joiValidator.body(Joi.object({
_css: Joi.string().allow(""),
name: Joi.string().required(),
route: Joi.string().required(),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_instanceName: Joi.string().required(),
_styles: Joi.object().required(),
type: Joi.string().optional(),
table: Joi.string().optional(),
}).required().unknown(true),
}).unknown(true))
}
function generatePatchValidation() {
return joiValidator.body(
Joi.object({
oldname: Joi.string().required(),
newname: Joi.string().required(),
}).unknown(true)
)
}
router.post(
"/_builder/api/:appId/pages/:pageName",
authorized(BUILDER),
async ctx => {
await buildPage(ctx.params.appId, ctx.params.pageName, ctx.request.body)
ctx.response.status = StatusCodes.OK
}
)
router.get(
"/_builder/api/:appId/pages/:pagename/screens",
authorized(BUILDER),
async ctx => {
ctx.body = await listScreens(ctx.params.appId, ctx.params.pagename)
ctx.response.status = StatusCodes.OK
}
)
router.post(
"/_builder/api/:appId/pages/:pagename/screen",
authorized(BUILDER),
generateSaveValidation(),
async ctx => {
ctx.body = await saveScreen(
ctx.config,
ctx.params.appId,
ctx.params.pagename,
ctx.request.body
)
ctx.response.status = StatusCodes.OK
}
)
router.patch(
"/_builder/api/:appname/pages/:pagename/screen",
authorized(BUILDER),
generatePatchValidation(),
async ctx => {
await renameScreen(
ctx.config,
ctx.params.appname,
ctx.params.pagename,
ctx.request.body.oldname,
ctx.request.body.newname
)
ctx.response.status = StatusCodes.OK
}
)
router.delete(
"/_builder/api/pages/:pagename/screens/:id",
authorized(BUILDER),
async ctx => {
await deleteScreen(
ctx.config,
ctx.user.appId,
ctx.params.pagename,
ctx.params.id
)
ctx.response.status = StatusCodes.OK
}
)
module.exports = router module.exports = router

View File

@ -1,10 +0,0 @@
const Router = require("@koa/router")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels")
const controller = require("../controllers/page")
const router = Router()
router.post("/api/pages/:pageId", authorized(BUILDER), controller.save)
module.exports = router

View File

@ -0,0 +1,108 @@
const Router = require("@koa/router")
const StatusCodes = require("../../utilities/statusCodes")
const joiValidator = require("../../middleware/joi-validator")
const Joi = require("joi")
const {
listScreens,
saveScreen,
buildPage,
renameScreen,
deleteScreen,
} = require("../../utilities/builder")
const authorized = require("../../middleware/authorized")
const { BUILDER } = require("../../utilities/accessLevels")
const router = Router()
function generateSaveValidation() {
// prettier-ignore
return joiValidator.body(Joi.object({
_css: Joi.string().allow(""),
name: Joi.string().required(),
route: Joi.string().required(),
props: Joi.object({
_id: Joi.string().required(),
_component: Joi.string().required(),
_children: Joi.array().required(),
_instanceName: Joi.string().required(),
_styles: Joi.object().required(),
type: Joi.string().optional(),
table: Joi.string().optional(),
}).required().unknown(true),
}).unknown(true))
}
function generatePatchValidation() {
return joiValidator.body(
Joi.object({
oldname: Joi.string().required(),
newname: Joi.string().required(),
}).unknown(true)
)
}
router.post(
"/_builder/api/:appId/pages/:pageName",
authorized(BUILDER),
async ctx => {
await buildPage(ctx.params.appId, ctx.params.pageName, ctx.request.body)
ctx.response.status = StatusCodes.OK
}
)
router.get(
"/_builder/api/:appId/pages/:pagename/screens",
authorized(BUILDER),
async ctx => {
ctx.body = await listScreens(ctx.params.appId, ctx.params.pagename)
ctx.response.status = StatusCodes.OK
}
)
router.post(
"/_builder/api/:appId/pages/:pagename/screen",
authorized(BUILDER),
generateSaveValidation(),
async ctx => {
ctx.body = await saveScreen(
ctx.config,
ctx.params.appId,
ctx.params.pagename,
ctx.request.body
)
ctx.response.status = StatusCodes.OK
}
)
router.patch(
"/_builder/api/:appname/pages/:pagename/screen",
authorized(BUILDER),
generatePatchValidation(),
async ctx => {
await renameScreen(
ctx.config,
ctx.params.appname,
ctx.params.pagename,
ctx.request.body.oldname,
ctx.request.body.newname
)
ctx.response.status = StatusCodes.OK
}
)
router.delete(
"/_builder/api/pages/:pagename/screens/:id",
authorized(BUILDER),
async ctx => {
await deleteScreen(
ctx.config,
ctx.user.appId,
ctx.params.pagename,
ctx.params.id
)
ctx.response.status = StatusCodes.OK
}
)
module.exports = router

View File

@ -146,7 +146,6 @@ const MAIN = {
className: "", className: "",
onLoad: [], onLoad: [],
}, },
uiFunctions: "",
} }
const UNAUTHENTICATED = { const UNAUTHENTICATED = {
@ -217,7 +216,6 @@ const UNAUTHENTICATED = {
className: "", className: "",
onLoad: [], onLoad: [],
}, },
uiFunctions: "",
} }
module.exports = { MAIN, UNAUTHENTICATED, PageTypes } module.exports = { MAIN, UNAUTHENTICATED, PageTypes }

View File

@ -139,6 +139,5 @@
"_code": "", "_code": "",
"className": "", "className": "",
"onLoad": [] "onLoad": []
}, }
"uiFunctions": ""
} }

View File

@ -63,6 +63,5 @@
"_code": "", "_code": "",
"className": "", "className": "",
"onLoad": [] "onLoad": []
}, }
"uiFunctions": ""
} }

View File

@ -75,10 +75,7 @@ const buildFrontendAppDefinition = async (appId, pageName, pkg) => {
const filename = join(appPublicPath, "clientFrontendDefinition.js") const filename = join(appPublicPath, "clientFrontendDefinition.js")
// TODO: weird - why
if (pkg.page._css) {
delete pkg.page._css delete pkg.page._css
}
for (let screen of pkg.screens) { for (let screen of pkg.screens) {
if (screen._css) { if (screen._css) {

View File

@ -1,20 +1,20 @@
const { readJSON, readdir } = require("fs-extra") // const { readJSON, readdir } = require("fs-extra")
const { join } = require("../centralPath") // const { join } = require("../centralPath")
module.exports = async appPath => { // module.exports = async appPath => {
const pages = {} // const pages = {}
const pageFolders = await readdir(join(appPath, "pages")) // const pageFolders = await readdir(join(appPath, "pages"))
for (let pageFolder of pageFolders) { // for (let pageFolder of pageFolders) {
try { // try {
pages[pageFolder] = await readJSON( // pages[pageFolder] = await readJSON(
join(appPath, "pages", pageFolder, "page.json") // join(appPath, "pages", pageFolder, "page.json")
) // )
pages[pageFolder].name = pageFolder // pages[pageFolder].name = pageFolder
} catch (_) { // } catch (_) {
// ignore error // // ignore error
} // }
} // }
return pages // return pages
} // }

View File

@ -1,24 +1,24 @@
const { appPackageFolder } = require("../createAppPackage") // const { appPackageFolder } = require("../createAppPackage")
const { // const {
readJSON, // readJSON,
writeJSON, // writeJSON,
readdir, // readdir,
ensureDir, // ensureDir,
rename, // rename,
unlink, // unlink,
rmdir, // rmdir,
} = require("fs-extra") // } = require("fs-extra")
const { join } = require("../centralPath") // const { join } = require("../centralPath")
const { dirname } = require("path") // const { dirname } = require("path")
const buildPage = require("./buildPage") const buildPage = require("./buildPage")
// const getPages = require("./getPages") // const getPages = require("./getPages")
const listScreens = require("./listScreens") // const listScreens = require("./listScreens")
const { budibaseAppsDir } = require("../budibaseDir") // const { budibaseAppsDir } = require("../budibaseDir")
// const { budibaseAppsDir } = require("../budibaseDir") // const { budibaseAppsDir } = require("../budibaseDir")
module.exports.buildPage = buildPage module.exports.buildPage = buildPage
module.exports.listScreens = listScreens // module.exports.listScreens = listScreens
// const getAppDefinition = async appPath => // const getAppDefinition = async appPath =>
// await readJSON(`${appPath}/appDefinition.json`) // await readJSON(`${appPath}/appDefinition.json`)
@ -34,53 +34,36 @@ module.exports.listScreens = listScreens
// } // }
// } // }
const screenPath = (appPath, pageName, name) => // const screenPath = (appPath, pageName, name) =>
join(appPath, "pages", pageName, "screens", name + ".json") // join(appPath, "pages", pageName, "screens", name + ".json")
module.exports.saveScreen = async (appId, pagename, screen) => { // module.exports.saveScreen = async (appId, pagename, screen) => {
const appPath = join(budibaseAppsDir(), appId) // const appPath = join(budibaseAppsDir(), appId)
const compPath = screenPath(appPath, pagename, screen.props._id) // const compPath = screenPath(appPath, pagename, screen.props._id)
await ensureDir(dirname(compPath)) // await ensureDir(dirname(compPath))
if (screen._css) { // if (screen._css) {
delete screen._css // delete screen._css
} // }
await writeJSON(compPath, screen, { // await writeJSON(compPath, screen, {
encoding: "utf8", // encoding: "utf8",
flag: "w", // flag: "w",
spaces: 2, // spaces: 2,
}) // })
return screen // return screen
} // }
module.exports.renameScreen = async ( // module.exports.deleteScreen = async (config, appId, pagename, name) => {
config, // const appPath = join(budibaseAppsDir(), appId)
appId, // const componentFile = screenPath(appPath, pagename, name)
pagename, // await unlink(componentFile)
oldName,
newName
) => {
const appPath = join(budibaseAppsDir(), appId)
const oldComponentPath = screenPath(appPath, pagename, oldName) // const dir = dirname(componentFile)
// if ((await readdir(dir)).length === 0) {
const newComponentPath = screenPath(appPath, pagename, newName) // await rmdir(dir)
// }
await ensureDir(dirname(newComponentPath)) // }
await rename(oldComponentPath, newComponentPath)
}
module.exports.deleteScreen = async (config, appId, pagename, name) => {
const appPath = join(budibaseAppsDir(), appId)
const componentFile = screenPath(appPath, pagename, name)
await unlink(componentFile)
const dir = dirname(componentFile)
if ((await readdir(dir)).length === 0) {
await rmdir(dir)
}
}
// module.exports.savePage = async (appId, pagename, page) => { // module.exports.savePage = async (appId, pagename, page) => {
// const appPath = join(budibaseAppsDir(), appId) // const appPath = join(budibaseAppsDir(), appId)