Updates backend routing
This merges in the changes to enable routing for the backend
This commit is contained in:
commit
5c16770e3b
|
@ -50,6 +50,7 @@
|
|||
"safe-buffer": "^5.1.2",
|
||||
"shortid": "^2.2.8",
|
||||
"string_decoder": "^1.2.0",
|
||||
"svelte-simple-modal": "^0.3.0",
|
||||
"uikit": "^3.1.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -58,7 +59,7 @@
|
|||
"@babel/preset-env": "^7.5.5",
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@rollup/plugin-alias": "^3.0.1",
|
||||
"@sveltech/routify": "1.5.0-beta.16",
|
||||
"@sveltech/routify": "1.5.0-beta.40",
|
||||
"babel-jest": "^24.8.0",
|
||||
"browser-sync": "^2.26.7",
|
||||
"http-proxy-middleware": "^0.19.1",
|
||||
|
|
|
@ -8,38 +8,10 @@ import { terser } from "rollup-plugin-terser"
|
|||
import builtins from "rollup-plugin-node-builtins"
|
||||
import nodeglobals from "rollup-plugin-node-globals"
|
||||
import copy from "rollup-plugin-copy"
|
||||
import browsersync from "rollup-plugin-browsersync"
|
||||
import proxy from "http-proxy-middleware"
|
||||
import replace from "rollup-plugin-replace"
|
||||
|
||||
import path from "path"
|
||||
|
||||
const target = "http://localhost:4001"
|
||||
const _builderProxy = proxy("/_builder", {
|
||||
target: "http://localhost:3000",
|
||||
pathRewrite: { "^/_builder": "" },
|
||||
})
|
||||
|
||||
const apiProxy = proxy(
|
||||
[
|
||||
"/_builder/assets/**",
|
||||
"/_builder/api/**",
|
||||
"/_builder/**/componentlibrary",
|
||||
"/_builder/instance/**",
|
||||
],
|
||||
{
|
||||
target,
|
||||
logLevel: "debug",
|
||||
changeOrigin: true,
|
||||
cookieDomainRewrite: true,
|
||||
onProxyReq(proxyReq) {
|
||||
if (proxyReq.getHeader("origin")) {
|
||||
proxyReq.setHeader("origin", target)
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH
|
||||
|
||||
const lodash_fp_exports = [
|
||||
|
@ -230,34 +202,9 @@ export default {
|
|||
// Watch the `dist` directory and refresh the
|
||||
// browser on changes when not in production
|
||||
!production && livereload(outputpath),
|
||||
!production &&
|
||||
browsersync({
|
||||
server: outputpath,
|
||||
middleware: [apiProxy, _builderProxy],
|
||||
}),
|
||||
|
||||
// If we're building for production (npm run build
|
||||
// instead of npm run dev), minify
|
||||
production && terser(),
|
||||
],
|
||||
watch: {
|
||||
clearScreen: false,
|
||||
},
|
||||
}
|
||||
|
||||
function serve() {
|
||||
let started = false
|
||||
|
||||
return {
|
||||
writeBundle() {
|
||||
if (!started) {
|
||||
started = true
|
||||
|
||||
require("child_process").spawn("npm", ["run", "start"], {
|
||||
stdio: ["ignore", "inherit", "inherit"],
|
||||
shell: true,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,17 +1,13 @@
|
|||
<script>
|
||||
import NoPackage from "./NoPackage.svelte"
|
||||
import PackageRoot from "./PackageRoot.svelte"
|
||||
import Settings from "./Settings.svelte"
|
||||
import { store, initialise } from "builderStore"
|
||||
import Modal from "svelte-simple-modal"
|
||||
import { onMount } from "svelte"
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
import { Router, basepath } from "@sveltech/routify"
|
||||
import { routes } from "@sveltech/routify/tmp/routes"
|
||||
import { store, initialise } from "builderStore"
|
||||
import AppNotification, {
|
||||
showAppNotification,
|
||||
} from "components/common/AppNotification.svelte"
|
||||
|
||||
let init = initialise()
|
||||
|
||||
function showErrorBanner() {
|
||||
showAppNotification({
|
||||
status: "danger",
|
||||
|
@ -24,57 +20,12 @@
|
|||
window.addEventListener("error", showErrorBanner)
|
||||
window.addEventListener("unhandledrejection", showErrorBanner)
|
||||
})
|
||||
|
||||
$basepath = "/_builder"
|
||||
</script>
|
||||
|
||||
<main>
|
||||
<AppNotification />
|
||||
{#await init}
|
||||
<div class="spinner-container">
|
||||
<Spinner />
|
||||
</div>
|
||||
{:then result}
|
||||
|
||||
{#if $store.hasAppPackage}
|
||||
<PackageRoot />
|
||||
{:else}
|
||||
<NoPackage />
|
||||
{/if}
|
||||
|
||||
{:catch err}
|
||||
<h1 style="color:red">{err}</h1>
|
||||
{/await}
|
||||
|
||||
<!--
|
||||
<div class="settings">
|
||||
<IconButton icon="settings"
|
||||
on:click={store.showSettings}/>
|
||||
</div>
|
||||
|
||||
|
||||
{#if $store.useAnalytics}
|
||||
<iframe src="https://marblekirby.github.io/bb-analytics.html" width="0" height="0" style="visibility:hidden;display:none"/>
|
||||
{/if}
|
||||
-->
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-family: "Roboto", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.settings {
|
||||
position: absolute;
|
||||
bottom: 25px;
|
||||
right: 25px;
|
||||
}
|
||||
|
||||
.spinner-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
<Modal>
|
||||
<Router {routes} />
|
||||
</Modal>
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
<script>
|
||||
import BackendNav from "components/nav/BackendNav.svelte"
|
||||
import SchemaManagementDrawer from "components/nav/SchemaManagementDrawer.svelte"
|
||||
import Database from "components/database/DatabaseRoot.svelte"
|
||||
import UserInterface from "components/userInterface/UserInterfaceRoot.svelte"
|
||||
import ActionsAndTriggers from "components/actionsAndTriggers/ActionsAndTriggersRoot.svelte"
|
||||
import AccessLevels from "components/accessLevels/AccessLevelsRoot.svelte"
|
||||
import ComingSoon from "components/common/ComingSoon.svelte"
|
||||
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="nav">
|
||||
<BackendNav />
|
||||
</div>
|
||||
<div class="content">
|
||||
{#if $backendUiStore.leftNavItem === 'DATABASE'}
|
||||
<Database />
|
||||
{:else if $backendUiStore.leftNavItem === 'ACTIONS'}
|
||||
<ActionsAndTriggers />
|
||||
{:else if $backendUiStore.leftNavItem === 'ACCESS_LEVELS'}
|
||||
<AccessLevels />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="nav">
|
||||
<SchemaManagementDrawer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
margin: 20px 40px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
overflow: auto;
|
||||
flex: 0 1 auto;
|
||||
width: 275px;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,172 +0,0 @@
|
|||
<script>
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
import { store } from "builderStore"
|
||||
import UserInterfaceRoot from "components/userInterface/UserInterfaceRoot.svelte"
|
||||
import BackendRoot from "./BackendRoot.svelte"
|
||||
import { fade } from "svelte/transition"
|
||||
import { SettingsIcon, PreviewIcon } from "components/common/Icons/"
|
||||
|
||||
const TABS = {
|
||||
BACKEND: "backend",
|
||||
FRONTEND: "frontend",
|
||||
}
|
||||
|
||||
let selectedTab = TABS.BACKEND
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
||||
<div class="top-nav">
|
||||
<div class="topleftnav">
|
||||
<button class="home-logo">
|
||||
<img src="/_builder/assets/budibase-emblem-white.svg" />
|
||||
</button>
|
||||
<!-- <IconButton icon="home"
|
||||
color="var(--slate)"
|
||||
hoverColor="var(--secondary75)"/> -->
|
||||
<span
|
||||
class:active={selectedTab === TABS.BACKEND}
|
||||
class="topnavitem"
|
||||
on:click={() => (selectedTab = TABS.BACKEND)}>
|
||||
Backend
|
||||
</span>
|
||||
<span
|
||||
class:active={selectedTab === TABS.FRONTEND}
|
||||
class="topnavitem"
|
||||
on:click={() => (selectedTab = TABS.FRONTEND)}>
|
||||
Frontend
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="toprightnav">
|
||||
<span
|
||||
class:active={selectedTab === TABS.FRONTEND}
|
||||
class="topnavitemright"
|
||||
on:click={() => selectedTab === TABS.FRONTEND}>
|
||||
<SettingsIcon />
|
||||
</span>
|
||||
<span
|
||||
class:active={selectedTab === TABS.FRONTEND}
|
||||
class="topnavitemright"
|
||||
on:click={() => selectedTab === TABS.FRONTEND}>
|
||||
<PreviewIcon />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{#if selectedTab === TABS.BACKEND}
|
||||
<div in:fade out:fade>
|
||||
<BackendRoot />
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade out:fade>
|
||||
<UserInterfaceRoot />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
flex: 0 0 auto;
|
||||
height: 48px;
|
||||
background: #0d203b;
|
||||
padding: 0px 20px 0 20px;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content > div {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toprightnav {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.topleftnav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.topnavitem {
|
||||
cursor: pointer;
|
||||
color: rgb(255, 255, 255, 0.6);
|
||||
margin: 0px 10px;
|
||||
padding-top: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.topnavitem:hover {
|
||||
color: rgb(255, 255, 255, 0.8);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.topnavitemright {
|
||||
cursor: pointer;
|
||||
color: rgb(255, 255, 255, 0.6);
|
||||
margin: 0px 5px;
|
||||
padding-top: 4px;
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.topnavitemright:hover {
|
||||
color: rgb(255, 255, 255, 0.8);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.home-logo {
|
||||
border-style: none;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
height: 40px;
|
||||
padding: 8px 10px 8px 0;
|
||||
}
|
||||
|
||||
.home-logo:hover {
|
||||
color: var(--hovercolor);
|
||||
}
|
||||
|
||||
.home-logo:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.home-logo img {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -10,20 +10,7 @@ export const initialise = async () => {
|
|||
if (process.env.NODE_ENV === "production") {
|
||||
LogRocket.init("knlald/budibase")
|
||||
}
|
||||
setupRouter(store)
|
||||
await store.initialise()
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
const setupRouter = writable => {
|
||||
const pushState = history.pushState
|
||||
history.pushState = () => {
|
||||
pushState.apply(history, [writable])
|
||||
writable.initialise()
|
||||
}
|
||||
window.addEventListener("hashchange", () => {
|
||||
writable.initialise()
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
|
||||
export const getBackendUiStore = () => {
|
||||
const INITIAL_BACKEND_UI_STATE = {
|
||||
leftNavItem: "DATABASE",
|
||||
selectedView: {
|
||||
records: [],
|
||||
name: "",
|
||||
|
@ -27,7 +26,6 @@ export const getBackendUiStore = () => {
|
|||
const store = writable(INITIAL_BACKEND_UI_STATE)
|
||||
|
||||
store.actions = {
|
||||
navigate: name => store.update(state => ({ ...state, leftNavItem: name })),
|
||||
database: {
|
||||
select: db =>
|
||||
store.update(state => {
|
||||
|
@ -60,10 +58,6 @@ export const getBackendUiStore = () => {
|
|||
return state
|
||||
}),
|
||||
},
|
||||
modals: {
|
||||
show: modal => store.update(state => ({ ...state, visibleModal: modal })),
|
||||
hide: () => store.update(state => ({ ...state, visibleModal: null })),
|
||||
},
|
||||
users: {
|
||||
create: user =>
|
||||
store.update(state => {
|
||||
|
@ -302,7 +296,7 @@ export const deleteLevel = store => level => {
|
|||
state.accessLevels.levels = state.accessLevels.levels.filter(
|
||||
t => t.name !== level.name
|
||||
)
|
||||
incrementAccessLevelsVersion(s)
|
||||
incrementAccessLevelsVersion(state)
|
||||
saveBackend(state)
|
||||
return state
|
||||
})
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//
|
||||
import { filter, cloneDeep, last, concat, isEmpty, values } from "lodash/fp"
|
||||
import { pipe, getNode, constructHierarchy } from "components/common/core"
|
||||
import * as backendStoreActions from "./backend"
|
||||
|
@ -49,7 +50,7 @@ export const getStore = () => {
|
|||
|
||||
const store = writable(initial)
|
||||
|
||||
store.initialise = initialise(store, initial)
|
||||
store.setPackage = setPackage(store, initial)
|
||||
|
||||
store.newChildRecord = backendStoreActions.newRecord(store, false)
|
||||
store.newRootRecord = backendStoreActions.newRecord(store, true)
|
||||
|
@ -100,26 +101,12 @@ export const getStore = () => {
|
|||
|
||||
export default getStore
|
||||
|
||||
const initialise = (store, initial) => async () => {
|
||||
appname = window.location.hash
|
||||
? last(window.location.hash.substr(1).split("/"))
|
||||
: ""
|
||||
|
||||
if (!appname) {
|
||||
initial.apps = await api.get(`/_builder/api/apps`).then(r => r.json())
|
||||
initial.hasAppPackage = false
|
||||
store.set(initial)
|
||||
return initial
|
||||
}
|
||||
|
||||
const pkg = await api
|
||||
.get(`/_builder/api/${appname}/appPackage`)
|
||||
.then(r => r.json())
|
||||
const setPackage = (store, initial) => async (pkg) => {
|
||||
|
||||
const [main_screens, unauth_screens] = await Promise.all([
|
||||
api.get(`/_builder/api/${appname}/pages/main/screens`).then(r => r.json()),
|
||||
api.get(`/_builder/api/${pkg.application.name}/pages/main/screens`).then(r => r.json()),
|
||||
api
|
||||
.get(`/_builder/api/${appname}/pages/unauthenticated/screens`)
|
||||
.get(`/_builder/api/${pkg.application.name}/pages/unauthenticated/screens`)
|
||||
.then(r => r.json()),
|
||||
])
|
||||
|
||||
|
@ -134,12 +121,12 @@ const initialise = (store, initial) => async () => {
|
|||
},
|
||||
}
|
||||
|
||||
initial.libraries = await loadLibs(appname, pkg)
|
||||
initial.libraries = await loadLibs(pkg.application.name, pkg)
|
||||
initial.loadLibraryUrls = pageName => {
|
||||
const libs = libUrlsForPreview(pkg, pageName)
|
||||
return libs
|
||||
}
|
||||
initial.appname = appname
|
||||
initial.appname = pkg.application.name
|
||||
initial.pages = pkg.pages
|
||||
initial.hasAppPackage = true
|
||||
initial.hierarchy = pkg.appDefinition.hierarchy
|
||||
|
@ -377,7 +364,7 @@ const savePage = store => async page => {
|
|||
|
||||
const addComponentLibrary = store => async lib => {
|
||||
const response = await api.get(
|
||||
`/_builder/api/${appname}/componentlibrary?lib=${encodeURI(lib)}`,
|
||||
`/_builder/api/${s.appname}/componentlibrary?lib=${encodeURI(lib)}`,
|
||||
undefined,
|
||||
false
|
||||
)
|
||||
|
@ -436,7 +423,7 @@ const removeStylesheet = store => stylesheet => {
|
|||
const _savePage = async s => {
|
||||
const page = s.pages[s.currentPageName]
|
||||
|
||||
await api.post(`/_builder/api/${appname}/pages/${s.currentPageName}`, {
|
||||
await api.post(`/_builder/api/${s.appname}/pages/${s.currentPageName}`, {
|
||||
page: { componentLibraries: s.pages.componentLibraries, ...page },
|
||||
uiFunctions: s.currentPageFunctions,
|
||||
screens: page._screens,
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
export let allLevels
|
||||
export let hierarchy
|
||||
export let actions
|
||||
export let close
|
||||
export let title
|
||||
|
||||
let errors = []
|
||||
let clonedLevel = cloneDeep(level)
|
||||
|
@ -52,6 +54,7 @@
|
|||
if (errors.length > 0) return
|
||||
|
||||
onFinished(clonedLevel)
|
||||
close()
|
||||
}
|
||||
|
||||
const permissionChanged = perm => ev => {
|
||||
|
@ -70,9 +73,13 @@
|
|||
|
||||
<div>
|
||||
|
||||
<div class="uk-modal-header">
|
||||
<h4 class="budibase__title--4">{title}</h4>
|
||||
</div>
|
||||
|
||||
<ErrorsBox {errors} />
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
<form on:submit|preventDefault class="uk-form-horizontal">
|
||||
|
||||
<Textbox label="Access Level Name" bind:text={clonedLevel.name} />
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
<ErrorsBox {errors} />
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
<form on:submit|preventDefault class="uk-form-horizontal">
|
||||
|
||||
<Textbox label="Name" bind:text={clonedAction.name} />
|
||||
<Textbox
|
||||
|
|
|
@ -22,10 +22,7 @@
|
|||
let cancel = () => onFinished()
|
||||
let save = () => {
|
||||
const newTriggersList = [
|
||||
...pipe(
|
||||
allTriggers,
|
||||
[filter(t => t !== trigger)]
|
||||
),
|
||||
...pipe(allTriggers, [filter(t => t !== trigger)]),
|
||||
clonedTrigger,
|
||||
]
|
||||
|
||||
|
@ -43,7 +40,7 @@
|
|||
|
||||
<ErrorsBox {errors} style="margin-bottom:20px" />
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
<form on:submit|preventDefault class="uk-form-horizontal">
|
||||
|
||||
<Dropdown
|
||||
label="Event"
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
<script>
|
||||
import ModelView from "./ModelView.svelte"
|
||||
import IndexView from "./IndexView.svelte"
|
||||
import ModelDataTable from "./ModelDataTable"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import getIcon from "components/common/icon"
|
||||
import DropdownButton from "components/common/DropdownButton.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import * as api from "./ModelDataTable/api"
|
||||
import {
|
||||
CreateEditRecordModal,
|
||||
CreateEditModelModal,
|
||||
CreateEditViewModal,
|
||||
CreateDatabaseModal,
|
||||
DeleteRecordModal,
|
||||
CreateUserModal,
|
||||
} from "./ModelDataTable/modals"
|
||||
|
||||
let selectedRecord
|
||||
|
||||
async function selectRecord(record) {
|
||||
selectedRecord = await api.loadRecord(record.key, {
|
||||
appname: $store.appname,
|
||||
instanceId: $backendUiStore.selectedDatabase.id,
|
||||
})
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
backendUiStore.actions.modals.hide()
|
||||
}
|
||||
|
||||
$: recordOpen = $backendUiStore.visibleModal === "RECORD"
|
||||
$: modelOpen = $backendUiStore.visibleModal === "MODEL"
|
||||
$: viewOpen = $backendUiStore.visibleModal === "VIEW"
|
||||
$: databaseOpen = $backendUiStore.visibleModal === "DATABASE"
|
||||
$: deleteRecordOpen = $backendUiStore.visibleModal === "DELETE_RECORD"
|
||||
$: userOpen = $backendUiStore.visibleModal === "USER"
|
||||
$: breadcrumbs = $backendUiStore.breadcrumbs.join(" / ")
|
||||
</script>
|
||||
|
||||
<Modal isOpen={!!$backendUiStore.visibleModal} {onClosed}>
|
||||
{#if recordOpen}
|
||||
<CreateEditRecordModal record={selectedRecord} {onClosed} />
|
||||
{/if}
|
||||
{#if modelOpen}
|
||||
<CreateEditModelModal {onClosed} />
|
||||
{/if}
|
||||
{#if viewOpen}
|
||||
<CreateEditViewModal {onClosed} />
|
||||
{/if}
|
||||
{#if databaseOpen}
|
||||
<CreateDatabaseModal {onClosed} />
|
||||
{/if}
|
||||
{#if deleteRecordOpen}
|
||||
<DeleteRecordModal record={selectedRecord} {onClosed} />
|
||||
{/if}
|
||||
{#if userOpen}
|
||||
<CreateUserModal {onClosed} />
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<div class="root">
|
||||
<div class="node-view">
|
||||
<div class="database-actions">
|
||||
<div class="breadcrumb">{breadcrumbs}</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ActionButton
|
||||
primary
|
||||
on:click={() => {
|
||||
selectedRecord = null
|
||||
backendUiStore.actions.modals.show('RECORD')
|
||||
}}>
|
||||
Create new record
|
||||
</ActionButton>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ModelDataTable {selectRecord} />
|
||||
{:else}Please select a database{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.node-view {
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--secondary60);
|
||||
}
|
||||
|
||||
.database-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
<ErrorsBox {errors} />
|
||||
|
||||
<form class="uk-form-stacked">
|
||||
<form on:submit|preventDefault class="uk-form-stacked">
|
||||
<Textbox label="Name" bind:text={clonedField.name} />
|
||||
<Dropdown
|
||||
label="Type"
|
||||
|
@ -136,18 +136,19 @@
|
|||
bind:value={clonedField.typeOptions.maxLength} />
|
||||
{/if}
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
<ActionButton primary on:click={save}>Save</ActionButton>
|
||||
<ActionButton alert on:click={() => onFinished(false)}>Cancel</ActionButton>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
margin: 20px;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
border-radius: 0 0 5px 5px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #fafafa;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<i class="ri-eye-line button--toggled" />
|
||||
<h3 class="budibase__title--3">Create / Edit View</h3>
|
||||
</heading>
|
||||
<form class="uk-form-stacked root">
|
||||
<form on:submit|preventDefault class="uk-form-stacked root">
|
||||
<h4 class="budibase__label--big">Settings</h4>
|
||||
{#if $store.errors && $store.errors.length > 0}
|
||||
<ErrorsBox errors={$store.errors} />
|
||||
|
@ -144,6 +144,7 @@
|
|||
}
|
||||
|
||||
heading {
|
||||
padding: 20px 20px 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { onMount, getContext } from "svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import {
|
||||
tap,
|
||||
|
@ -17,10 +17,39 @@
|
|||
import { getIndexSchema } from "components/common/core"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import TablePagination from "./TablePagination.svelte"
|
||||
import { DeleteRecordModal } from "./modals"
|
||||
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
|
||||
import * as api from "./api"
|
||||
|
||||
export let selectRecord
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
const editRecord = async row => {
|
||||
open(
|
||||
CreateEditRecordModal,
|
||||
{
|
||||
onClosed: close,
|
||||
record: await selectRecord(row),
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
|
||||
const deleteRecord = async row => {
|
||||
open(
|
||||
DeleteRecordModal,
|
||||
{
|
||||
onClosed: close,
|
||||
record: await selectRecord(row),
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
|
||||
async function selectRecord(record) {
|
||||
return await api.loadRecord(record.key, {
|
||||
appname: $store.appname,
|
||||
instanceId: $backendUiStore.selectedDatabase.id,
|
||||
})
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE = 10
|
||||
// Internal headers we want to hide from the user
|
||||
|
@ -56,11 +85,7 @@
|
|||
|
||||
const getSchema = getIndexSchema($store.hierarchy)
|
||||
|
||||
const childViewsForRecord = compose(
|
||||
flatten,
|
||||
map("indexes"),
|
||||
get("children")
|
||||
)
|
||||
const childViewsForRecord = compose(flatten, map("indexes"), get("children"))
|
||||
|
||||
const hideInternalHeaders = compose(
|
||||
remove(headerName => INTERNAL_HEADERS.includes(headerName)),
|
||||
|
@ -133,16 +158,14 @@
|
|||
</li>
|
||||
<li
|
||||
on:click={() => {
|
||||
selectRecord(row)
|
||||
backendUiStore.actions.modals.show('RECORD')
|
||||
editRecord(row)
|
||||
}}>
|
||||
<div>Edit</div>
|
||||
</li>
|
||||
<li>
|
||||
<div
|
||||
on:click={() => {
|
||||
selectRecord(row)
|
||||
backendUiStore.actions.modals.show('DELETE_RECORD')
|
||||
deleteRecord(row)
|
||||
}}>
|
||||
Delete
|
||||
</div>
|
||||
|
@ -166,7 +189,6 @@
|
|||
</section>
|
||||
|
||||
<style>
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import { store } from "builderStore"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import * as api from "../api"
|
||||
|
||||
|
@ -15,24 +15,26 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<section>
|
||||
Database Name
|
||||
<input class="uk-input" type="text" bind:value={databaseName} />
|
||||
</section>
|
||||
<footer>
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton disabled={!databaseName} on:click={createDatabase}>
|
||||
Save
|
||||
</ActionButton>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
section {
|
||||
padding: 30px;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #fafafa;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
<script>
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import ModelView from "../../ModelView.svelte"
|
||||
import * as api from "../api"
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import { onMount } from "svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import { compose, map, get, flatten } from "lodash/fp"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import Select from "components/common/Select.svelte"
|
||||
import {
|
||||
|
@ -68,11 +67,10 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="actions">
|
||||
<h4 class="budibase__title--4">Create / Edit Record</h4>
|
||||
<ErrorsBox {errors} />
|
||||
<div class="actions">
|
||||
<form class="uk-form-stacked">
|
||||
<form on:submit|preventDefault class="uk-form-stacked">
|
||||
{#if !record}
|
||||
<div class="uk-margin">
|
||||
<label class="uk-form-label" for="form-stacked-text">Model</label>
|
||||
|
@ -87,20 +85,19 @@
|
|||
<RecordFieldControl record={editingRecord} {field} {errors} />
|
||||
{/each}
|
||||
</form>
|
||||
</div>
|
||||
<footer>
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton on:click={saveRecord}>Save</ActionButton>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.actions {
|
||||
padding: 30px;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #fafafa;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import IndexView from "../../IndexView.svelte"
|
||||
import * as api from "../api"
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import * as api from "../api"
|
||||
|
@ -29,7 +28,8 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<form class="uk-form-stacked">
|
||||
<form on:submit|preventDefault class="uk-form-stacked">
|
||||
<div>
|
||||
<label class="uk-form-label" for="form-stacked-text">Username</label>
|
||||
<input class="uk-input" type="text" bind:value={username} />
|
||||
<label class="uk-form-label" for="form-stacked-text">Password</label>
|
||||
|
@ -40,6 +40,7 @@
|
|||
<option value={level.name}>{level.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<footer>
|
||||
<ActionButton alert on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton disabled={!valid} on:click={createUser}>Save</ActionButton>
|
||||
|
@ -47,13 +48,13 @@
|
|||
</form>
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding: 30px;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
padding: 20px;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #fafafa;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
select {
|
||||
width: 100%;
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
<script>
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import * as api from "../api"
|
||||
|
||||
export let record
|
||||
export let onClosed
|
||||
|
||||
$: currentAppInfo = {
|
||||
appname: $store.appname,
|
||||
instanceId: $backendUiStore.selectedDatabase.id,
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
backendUiStore.actions.modals.hide()
|
||||
}
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<div class="content">
|
||||
<heading>
|
||||
<i class="ri-information-line alert" />
|
||||
<h4 class="budibase__title--4">Delete Record</h4>
|
||||
|
@ -25,6 +22,7 @@
|
|||
Are you sure you want to delete this record? All of your data will be
|
||||
permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<ActionButton on:click={onClosed}>Cancel</ActionButton>
|
||||
<ActionButton
|
||||
|
@ -48,18 +46,17 @@
|
|||
|
||||
.modal-actions {
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: #fafafa;
|
||||
border-top: 1px solid #ccc;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
heading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.content {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0 0 0 10px;
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import getIcon from "components/common/icon"
|
||||
import FieldView from "./FieldView.svelte"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import {
|
||||
get,
|
||||
compose,
|
||||
|
@ -101,7 +100,6 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<heading>
|
||||
{#if !editingField}
|
||||
<i class="ri-list-settings-line button--toggled" />
|
||||
|
@ -112,13 +110,14 @@
|
|||
{/if}
|
||||
</heading>
|
||||
{#if !editingField}
|
||||
<div class="padding">
|
||||
<h4 class="budibase__label--big">Settings</h4>
|
||||
|
||||
{#if $store.errors && $store.errors.length > 0}
|
||||
<ErrorsBox errors={$store.errors} />
|
||||
{/if}
|
||||
|
||||
<form class="uk-form-stacked">
|
||||
<form on:submit|preventDefault class="uk-form-stacked">
|
||||
|
||||
<Textbox label="Name" bind:text={record.name} on:change={nameChanged} />
|
||||
{#if isChildModel}
|
||||
|
@ -186,6 +185,7 @@
|
|||
</ActionButton>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<FieldView
|
||||
field={fieldToEdit}
|
||||
|
@ -193,11 +193,10 @@
|
|||
allFields={record.fields}
|
||||
store={$store} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
.padding {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.new-field {
|
||||
|
@ -226,6 +225,7 @@
|
|||
}
|
||||
|
||||
heading {
|
||||
padding: 20px 20px 0 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,30 @@
|
|||
import UsersList from "./UsersList.svelte"
|
||||
import NavItem from "./NavItem.svelte"
|
||||
import getIcon from "components/common/icon"
|
||||
import {
|
||||
CreateDatabaseModal,
|
||||
CreateUserModal,
|
||||
} from "components/database/ModelDataTable/modals"
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
const openDatabaseCreator = () => {
|
||||
open(
|
||||
CreateDatabaseModal,
|
||||
{
|
||||
onClosed: close,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
const openUserCreator = () => {
|
||||
open(
|
||||
CreateUserModal,
|
||||
{
|
||||
onClosed: close,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="items-root">
|
||||
|
@ -13,9 +37,7 @@
|
|||
<div class="components-list-container">
|
||||
<div class="nav-group-header">
|
||||
<div class="hierarchy-title">Databases</div>
|
||||
<i
|
||||
class="ri-add-line hoverable"
|
||||
on:click={() => backendUiStore.actions.modals.show('DATABASE')} />
|
||||
<i class="ri-add-line hoverable" on:click={openDatabaseCreator} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -29,9 +51,7 @@
|
|||
<div class="components-list-container">
|
||||
<div class="nav-group-header">
|
||||
<div class="hierarchy-title">Users</div>
|
||||
<i
|
||||
class="ri-add-line hoverable"
|
||||
on:click={() => backendUiStore.actions.modals.show('USER')} />
|
||||
<i class="ri-add-line hoverable" on:click={openUserCreator} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -41,7 +61,10 @@
|
|||
|
||||
</div>
|
||||
{/if}
|
||||
<NavItem name="ACCESS_LEVELS" label="User Access Levels" />
|
||||
<NavItem
|
||||
name="ACCESS_LEVELS"
|
||||
label="User Access Levels"
|
||||
href="./accesslevels" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
<script>
|
||||
import { tick, onMount } from "svelte"
|
||||
import { goto } from "@sveltech/routify"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import api from "builderStore/api"
|
||||
import getIcon from "../common/icon"
|
||||
import { CheckIcon } from "../common/Icons"
|
||||
|
||||
$: instances = $store.appInstances
|
||||
$: views = $store.hierarchy.indexes
|
||||
|
||||
async function selectDatabase(database) {
|
||||
backendUiStore.actions.navigate("DATABASE")
|
||||
backendUiStore.actions.records.select(null)
|
||||
backendUiStore.actions.views.select(views[0])
|
||||
backendUiStore.actions.database.select(database)
|
||||
|
@ -25,12 +24,6 @@
|
|||
return state
|
||||
})
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if ($store.appInstances.length > 0) {
|
||||
selectDatabase($store.appInstances[0])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
@ -44,7 +37,9 @@
|
|||
</span>
|
||||
<button
|
||||
class:active={database.id === $backendUiStore.selectedDatabase.id}
|
||||
on:click={() => selectDatabase(database)}>
|
||||
on:click={() => {
|
||||
$goto(`./database/${database.id}`), selectDatabase(database)
|
||||
}}>
|
||||
{database.name}
|
||||
</button>
|
||||
<i
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
import { store, backendUiStore } from "builderStore"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import getIcon from "../common/icon"
|
||||
import {
|
||||
CreateEditModelModal,
|
||||
CreateEditViewModal,
|
||||
} from "components/database/ModelDataTable/modals"
|
||||
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
export let level = 0
|
||||
export let node
|
||||
|
@ -23,8 +29,15 @@
|
|||
|
||||
function selectHierarchyItem(node) {
|
||||
store.selectExistingNode(node.nodeId)
|
||||
const modalType = node.type === "index" ? "VIEW" : "MODEL"
|
||||
backendUiStore.actions.modals.show(modalType)
|
||||
const modalType =
|
||||
node.type === "index" ? CreateEditViewModal : CreateEditModelModal
|
||||
open(
|
||||
modalType,
|
||||
{
|
||||
onClosed: close,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
<script>
|
||||
import getIcon from "../common/icon"
|
||||
import { backendUiStore } from "builderStore"
|
||||
import { isActive, url, goto } from "@sveltech/routify"
|
||||
|
||||
export let name = ""
|
||||
export let label = ""
|
||||
|
||||
$: navActive = $backendUiStore.leftNavItem === name
|
||||
|
||||
const setActive = () => backendUiStore.actions.navigate(name)
|
||||
export let href
|
||||
</script>
|
||||
|
||||
<div
|
||||
on:click={() => $goto(href)}
|
||||
class="budibase__nav-item backend-nav-item"
|
||||
class:selected={navActive}
|
||||
on:click={setActive}>
|
||||
class:selected={$isActive(href)}>
|
||||
{label}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.backend-nav-item {
|
||||
padding-left: 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import HierarchyRow from "./HierarchyRow.svelte"
|
||||
import DropdownButton from "components/common/DropdownButton.svelte"
|
||||
import NavItem from "./NavItem.svelte"
|
||||
import getIcon from "components/common/icon"
|
||||
import {
|
||||
CreateEditModelModal,
|
||||
CreateEditViewModal,
|
||||
} from "components/database/ModelDataTable/modals"
|
||||
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
function newModel() {
|
||||
if ($store.currentNode) {
|
||||
|
@ -11,12 +18,24 @@
|
|||
} else {
|
||||
store.newRootRecord()
|
||||
}
|
||||
backendUiStore.actions.modals.show("MODEL")
|
||||
open(
|
||||
CreateEditModelModal,
|
||||
{
|
||||
onClosed: close,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
|
||||
function newView() {
|
||||
store.newRootIndex()
|
||||
backendUiStore.actions.modals.show("VIEW")
|
||||
open(
|
||||
CreateEditViewModal,
|
||||
{
|
||||
onClosed: close,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<script>
|
||||
import Button from "components/common/Button.svelte"
|
||||
import { store } from "builderStore"
|
||||
|
||||
let errors = []
|
||||
export let apps
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
|
@ -12,11 +10,10 @@
|
|||
class="logo"
|
||||
alt="budibase logo" />
|
||||
<div>
|
||||
|
||||
<div>
|
||||
<h4 style="margin-bottom: 20px">Choose an Application</h4>
|
||||
{#each $store.apps as app}
|
||||
<a href={`#${app}`} class="app-link">{app}</a>
|
||||
{#each apps as app}
|
||||
<a href={`/_builder/${app}`} class="app-link">{app}</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div class="root">
|
||||
|
||||
<form class="uk-search uk-search-large">
|
||||
<form on:submit|preventDefault class="uk-search uk-search-large">
|
||||
<span uk-search-icon />
|
||||
<input
|
||||
class="uk-search-input"
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
import Dropdown from "components/common/Dropdown.svelte"
|
||||
import PlusButton from "components/common/PlusButton.svelte"
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import EventEditorModal from "./EventEditorModal.svelte"
|
||||
import HandlerSelector from "./HandlerSelector.svelte"
|
||||
|
||||
|
@ -64,7 +63,7 @@
|
|||
</header>
|
||||
|
||||
<div class="root">
|
||||
<form class="uk-form-stacked form-root">
|
||||
<form on:submit|preventDefault class="uk-form-stacked form-root">
|
||||
{#each events as event, index}
|
||||
{#if event.handlers.length > 0}
|
||||
<div
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
<h3>{$store.currentPageName}</h3>
|
||||
|
||||
<form class="uk-form-horizontal">
|
||||
<form on:submit|preventDefault class="uk-form-horizontal">
|
||||
<Textbox bind:text={title} label="Title" hasError={!title} />
|
||||
<div class="help-text">
|
||||
The title of your page, displayed in the bowser tab
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<div class="root">
|
||||
|
||||
<form class="uk-form-stacked form-root">
|
||||
<form on:submit|preventDefault class="uk-form-stacked form-root">
|
||||
{#if componentDef}
|
||||
{#each Object.entries(componentDef.props) as [prop_name, prop_def], index}
|
||||
<div class="prop-container">
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
import PagesList from "./PagesList.svelte"
|
||||
import { store } from "builderStore"
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import NewScreen from "./NewScreen.svelte"
|
||||
import CurrentItemPreview from "./CurrentItemPreview.svelte"
|
||||
import SettingsView from "./SettingsView.svelte"
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
}
|
||||
|
||||
html, body {
|
||||
display: grid;
|
||||
font-family: var(--fontnormal);
|
||||
color: var(--secondary80);
|
||||
padding: 0;
|
||||
|
|
|
@ -1,10 +1,28 @@
|
|||
<script>
|
||||
import { store } from "builderStore"
|
||||
|
||||
import { fade } from "svelte/transition"
|
||||
import { isActive, goto, url, context } from "@sveltech/routify"
|
||||
import { isActive, goto, context } from "@sveltech/routify"
|
||||
|
||||
import { SettingsIcon, PreviewIcon } from "../../common/Icons/"
|
||||
import IconButton from "../../common/IconButton.svelte"
|
||||
import { SettingsIcon, PreviewIcon } from "components/common/Icons/"
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
|
||||
// Get Package and set store
|
||||
export let application
|
||||
|
||||
let promise = getPackage()
|
||||
|
||||
async function getPackage() {
|
||||
const res = await fetch(`/_builder/api/${application}/appPackage`)
|
||||
const pkg = await res.json()
|
||||
|
||||
if (res.ok) {
|
||||
await store.setPackage(pkg)
|
||||
return pkg
|
||||
} else {
|
||||
throw new Error(pkg)
|
||||
}
|
||||
}
|
||||
$: ({ component } = $context)
|
||||
$: list = component.parent.children.filter(child => child.isIndexable)
|
||||
</script>
|
||||
|
@ -19,12 +37,12 @@
|
|||
alt="budibase icon" />
|
||||
</button>
|
||||
|
||||
<!-- List is an array of subfolders in the application folder. -->
|
||||
<!-- This gets all indexable subroutes and sticks them in the top nav. -->
|
||||
{#each list as { path, prettyName, children, meta }}
|
||||
<span
|
||||
class:active={$isActive(path)}
|
||||
class="topnavitem"
|
||||
on:click={() => $goto($url(path))}>
|
||||
on:click={() => $goto(path)}>
|
||||
{prettyName}
|
||||
</span>
|
||||
{/each}
|
||||
|
@ -34,9 +52,9 @@
|
|||
</div>
|
||||
<div class="toprightnav">
|
||||
<span
|
||||
class:active={$isActive(`${component.parent.path}/settings`)}
|
||||
class:active={$isActive(`/settings`)}
|
||||
class="topnavitemright"
|
||||
on:click={() => $goto(`${component.parent.path}/settings`)}>
|
||||
on:click={() => $goto(`/settings`)}>
|
||||
<SettingsIcon />
|
||||
</span>
|
||||
<span
|
||||
|
@ -48,7 +66,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{#await promise}
|
||||
<!-- This should probably be some kind of loading state? -->
|
||||
<div />
|
||||
{:then}
|
||||
<slot />
|
||||
{/await}
|
||||
|
||||
</div>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import * as api from "components/database/ModelDataTable/api"
|
||||
|
||||
import BackendNav from "components/nav/BackendNav.svelte"
|
||||
import SchemaManagementDrawer from "components/nav/SchemaManagementDrawer.svelte"
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="nav">
|
||||
<BackendNav />
|
||||
</div>
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="nav">
|
||||
<SchemaManagementDrawer />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
margin: 20px 40px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
overflow: auto;
|
||||
flex: 0 1 auto;
|
||||
width: 275px;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,4 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
||||
import Button from "components/common/Button.svelte"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
|
@ -8,39 +11,34 @@
|
|||
getNewAccessLevel,
|
||||
} from "components/common/core"
|
||||
import getIcon from "components/common/icon"
|
||||
import AccessLevelView from "./AccessLevelView.svelte"
|
||||
import Modal from "components/common/Modal.svelte"
|
||||
import AccessLevelView from "components/accessLevels/AccessLevelView.svelte"
|
||||
|
||||
let editingLevel = null
|
||||
let editingLevelIsNew = false
|
||||
$: {
|
||||
if (editingLevel !== null) {
|
||||
backendUiStore.actions.modals.show("ACCESS_LEVELS")
|
||||
}
|
||||
}
|
||||
$: modalOpen = $backendUiStore.visibleModal === "ACCESS_LEVELS"
|
||||
|
||||
let allPermissions = []
|
||||
store.subscribe(db => {
|
||||
allPermissions = generateFullPermissions(db.hierarchy, db.actions)
|
||||
})
|
||||
|
||||
let onLevelEdit = level => {
|
||||
const openModal = (level, newLevel) => {
|
||||
editingLevel = level
|
||||
editingLevelIsNew = false
|
||||
editingLevelIsNew = newLevel
|
||||
open(AccessLevelView, {
|
||||
level: editingLevel,
|
||||
allPermissions,
|
||||
onFinished: onEditingFinished,
|
||||
isNew: editingLevelIsNew,
|
||||
allLevels: $store.accessLevels.levels,
|
||||
hierarchy: $store.hierarchy,
|
||||
actions: $store.actions,
|
||||
close: close,
|
||||
title: "Access Level",
|
||||
})
|
||||
}
|
||||
|
||||
let onLevelCancel = () => {
|
||||
let cancel = () => {
|
||||
editingAction = null
|
||||
}
|
||||
|
||||
let onLevelDelete = level => {
|
||||
store.deleteLevel(level)
|
||||
}
|
||||
|
||||
let createNewLevel = () => {
|
||||
editingLevelIsNew = true
|
||||
editingLevel = getNewAccessLevel()
|
||||
close()
|
||||
}
|
||||
|
||||
let onEditingFinished = level => {
|
||||
|
@ -48,7 +46,7 @@
|
|||
store.saveLevel(level, editingLevelIsNew, editingLevel)
|
||||
}
|
||||
editingLevel = null
|
||||
backendUiStore.actions.modals.hide()
|
||||
close()
|
||||
}
|
||||
|
||||
const getPermissionsString = perms => {
|
||||
|
@ -58,7 +56,7 @@
|
|||
|
||||
<div class="root">
|
||||
<ButtonGroup>
|
||||
<ActionButton primary on:click={createNewLevel}>
|
||||
<ActionButton primary on:click={() => openModal(getNewAccessLevel(), true)}>
|
||||
Create New Access Level
|
||||
</ActionButton>
|
||||
</ButtonGroup>
|
||||
|
@ -78,10 +76,10 @@
|
|||
<td>{level.name}</td>
|
||||
<td>{getPermissionsString(level.permissions)}</td>
|
||||
<td class="edit-button">
|
||||
<span on:click={() => onLevelEdit(level)}>
|
||||
<span on:click={() => openModal(level, false)}>
|
||||
{@html getIcon('edit')}
|
||||
</span>
|
||||
<span on:click={() => onLevelDelete(level)}>
|
||||
<span on:click={() => store.deleteLevel(level)}>
|
||||
{@html getIcon('trash')}
|
||||
</span>
|
||||
</td>
|
||||
|
@ -91,20 +89,6 @@
|
|||
</table>
|
||||
{:else}(no actions added){/if}
|
||||
|
||||
<Modal
|
||||
onClosed={backendUiStore.actions.modals.hide}
|
||||
bind:isOpen={modalOpen}
|
||||
title={modalOpen ? 'Edit Access Level' : 'Create Access Level'}>
|
||||
<AccessLevelView
|
||||
level={editingLevel}
|
||||
{allPermissions}
|
||||
onFinished={onEditingFinished}
|
||||
isNew={editingLevelIsNew}
|
||||
allLevels={$store.accessLevels.levels}
|
||||
hierarchy={$store.hierarchy}
|
||||
actions={$store.actions} />
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
|
@ -0,0 +1,54 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import ModelDataTable from "components/database/ModelDataTable"
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import ActionButton from "components/common/ActionButton.svelte"
|
||||
import * as api from "components/database/ModelDataTable/api"
|
||||
import { CreateEditRecordModal } from "components/database/ModelDataTable/modals"
|
||||
|
||||
const { open, close } = getContext("simple-modal")
|
||||
|
||||
const createNewRecord = () => {
|
||||
selectedRecord = null
|
||||
open(
|
||||
CreateEditRecordModal,
|
||||
{
|
||||
onClosed: close,
|
||||
record: selectedRecord,
|
||||
},
|
||||
{ styleContent: { padding: "0" } }
|
||||
)
|
||||
}
|
||||
|
||||
export let selectedDatabase
|
||||
|
||||
let selectedRecord
|
||||
|
||||
async function selectRecord(record) {
|
||||
selectedRecord = await api.loadRecord(record.key, {
|
||||
appname: $store.appname,
|
||||
instanceId: selectedDatabase,
|
||||
})
|
||||
}
|
||||
|
||||
$: breadcrumbs = $backendUiStore.breadcrumbs.join(" / ")
|
||||
</script>
|
||||
|
||||
<div class="database-actions">
|
||||
<div class="budibase__label--big">{breadcrumbs}</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ActionButton primary on:click={createNewRecord}>
|
||||
Create new record
|
||||
</ActionButton>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ModelDataTable {selectRecord} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.database-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,39 @@
|
|||
<script>
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import { goto } from "@sveltech/routify"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
$: instances = $store.appInstances
|
||||
$: views = $store.hierarchy.indexes
|
||||
|
||||
async function selectDatabase(database) {
|
||||
backendUiStore.actions.records.select(null)
|
||||
backendUiStore.actions.views.select(views[0])
|
||||
backendUiStore.actions.database.select(database)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if ($store.appInstances.length > 0) {
|
||||
await selectDatabase($store.appInstances[0])
|
||||
$goto(`./${$backendUiStore.selectedDatabase.id}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="node-view">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.node-view {
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,23 @@
|
|||
<script>
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import { goto } from "@sveltech/routify"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
$: instances = $store.appInstances
|
||||
$: views = $store.hierarchy.indexes
|
||||
|
||||
async function selectDatabase(database) {
|
||||
backendUiStore.actions.records.select(null)
|
||||
backendUiStore.actions.views.select(views[0])
|
||||
backendUiStore.actions.database.select(database)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if ($store.appInstances.length > 0) {
|
||||
await selectDatabase($store.appInstances[0])
|
||||
$goto(`../${$backendUiStore.selectedDatabase.id}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
Please select a database
|
|
@ -3,4 +3,4 @@
|
|||
$goto("../database")
|
||||
</script>
|
||||
|
||||
<!-- routify:options $index=false -->
|
||||
<!-- routify:options index=false -->
|
|
@ -0,0 +1,22 @@
|
|||
<script>
|
||||
import { store, backendUiStore } from "builderStore"
|
||||
import { goto } from "@sveltech/routify"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
$: instances = $store.appInstances
|
||||
$: views = $store.hierarchy.indexes
|
||||
|
||||
async function selectDatabase(database) {
|
||||
backendUiStore.actions.records.select(null)
|
||||
backendUiStore.actions.views.select(views[0])
|
||||
backendUiStore.actions.database.select(database)
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
if ($store.appInstances.length > 0 && !$backendUiStore.database) {
|
||||
await selectDatabase($store.appInstances[0])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<slot />
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import UserInterfaceRoot from "components/userInterface/UserInterfaceRoot.svelte"
|
||||
</script>
|
||||
|
||||
<UserInterfaceRoot />
|
|
@ -3,4 +3,4 @@
|
|||
$goto("../backend")
|
||||
</script>
|
||||
|
||||
<!-- routify:options $index=false -->
|
||||
<!-- routify:options index=false -->
|
|
@ -0,0 +1,2 @@
|
|||
<!-- routify:options index=false -->
|
||||
<slot />
|
|
@ -1,48 +0,0 @@
|
|||
<script>
|
||||
import BackendNav from "../../../nav/BackendNav.svelte"
|
||||
import SchemaManagementDrawer from "../../../nav/SchemaManagementDrawer.svelte"
|
||||
import Database from "../../../database/DatabaseRoot.svelte"
|
||||
import UserInterface from "../../../userInterface/UserInterfaceRoot.svelte"
|
||||
import ActionsAndTriggers from "../../../actionsAndTriggers/ActionsAndTriggersRoot.svelte"
|
||||
import AccessLevels from "../../../accessLevels/AccessLevelsRoot.svelte"
|
||||
import ComingSoon from "../../../common/ComingSoon.svelte"
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="nav">
|
||||
<BackendNav />
|
||||
</div>
|
||||
<div class="content">
|
||||
<!-- {#if $backendUiStore.leftNavItem === 'DATABASE'}
|
||||
<Database />
|
||||
{:else if $backendUiStore.leftNavItem === 'ACTIONS'}
|
||||
<ActionsAndTriggers />
|
||||
{:else if $backendUiStore.leftNavItem === 'ACCESS_LEVELS'}
|
||||
<AccessLevels />
|
||||
{/if} -->
|
||||
<slot />
|
||||
</div>
|
||||
<div class="nav">
|
||||
<!-- <SchemaManagementDrawer /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1 1 auto;
|
||||
margin: 80px 60px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
overflow: auto;
|
||||
flex: 0 1 auto;
|
||||
width: 300px;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -1,134 +0,0 @@
|
|||
<script>
|
||||
import getIcon from "../../../../common/icon"
|
||||
import Button from "../../../../common/Button.svelte"
|
||||
import ActionButton from "../../../../common/ActionButton.svelte"
|
||||
import ButtonGroup from "../../../../common/ButtonGroup.svelte"
|
||||
import Actions from "../../../../actionsAndTriggers/Actions.svelte"
|
||||
import Triggers from "../../../../actionsAndTriggers/Triggers.svelte"
|
||||
import { getNewAction, getNewTrigger } from "../../../../common/core"
|
||||
|
||||
let editingAction = null
|
||||
let editingActionIsNew = true
|
||||
let editingTrigger = null
|
||||
let editingTriggerIsNew = true
|
||||
|
||||
let getDefaultOptionsHtml = defaultOptions =>
|
||||
pipe(
|
||||
defaultOptions,
|
||||
[
|
||||
keys,
|
||||
map(
|
||||
k =>
|
||||
`<span style="color:var(--slate)">${k}: </span>${JSON.parse(
|
||||
typeOptions[k]
|
||||
)}`
|
||||
),
|
||||
join("<br>"),
|
||||
]
|
||||
)
|
||||
|
||||
let onActionEdit = action => {
|
||||
editingAction = action
|
||||
editingActionIsNew = false
|
||||
}
|
||||
|
||||
let newAction = () => {
|
||||
editingAction = getNewAction()
|
||||
editingActionIsNew = true
|
||||
}
|
||||
|
||||
let onActionDelete = action => {
|
||||
store.deleteAction(action)
|
||||
}
|
||||
|
||||
let deleteTrigger = () => {}
|
||||
|
||||
let editTrigger = trigger => {
|
||||
editingTrigger = trigger
|
||||
editingTriggerIsNew = false
|
||||
}
|
||||
|
||||
let newTrigger = () => {
|
||||
editingTrigger = getNewTrigger()
|
||||
editingTriggerIsNew = true
|
||||
}
|
||||
|
||||
let onActionSave = action => {
|
||||
store.saveAction(action, editingActionIsNew, editingAction)
|
||||
|
||||
editingAction = null
|
||||
}
|
||||
|
||||
let onActionCancel = () => {
|
||||
editingAction = null
|
||||
}
|
||||
|
||||
let onTriggerSave = trigger => {
|
||||
store.saveTrigger(trigger, editingTriggerIsNew, editingTrigger)
|
||||
|
||||
editingTrigger = null
|
||||
}
|
||||
|
||||
let onTriggerCancel = () => {
|
||||
editingTrigger = null
|
||||
}
|
||||
|
||||
let onTriggerEdit = trigger => {
|
||||
editingTrigger = trigger
|
||||
editingTriggerIsNew = false
|
||||
}
|
||||
|
||||
let onTriggerDelete = trigger => {
|
||||
store.deleteTrigger(trigger)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<div class="actions-header">
|
||||
<ButtonGroup>
|
||||
<ActionButton color="secondary" grouped on:click={newAction}>
|
||||
Create New Action
|
||||
</ActionButton>
|
||||
<ActionButton color="tertiary" grouped on:click={newTrigger}>
|
||||
Create New Trigger
|
||||
</ActionButton>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
||||
<div class="node-view">
|
||||
<Actions
|
||||
{editingActionIsNew}
|
||||
{editingAction}
|
||||
{onActionEdit}
|
||||
{onActionDelete}
|
||||
{onActionSave}
|
||||
{onActionCancel} />
|
||||
|
||||
<Triggers
|
||||
{editingTriggerIsNew}
|
||||
{editingTrigger}
|
||||
{onTriggerEdit}
|
||||
{onTriggerDelete}
|
||||
{onTriggerSave}
|
||||
{onTriggerCancel} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.actions-header {
|
||||
flex: 0 1 auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.node-view {
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
|
@ -1,96 +0,0 @@
|
|||
<script>
|
||||
import ModelView from "../../../../database/ModelView.svelte"
|
||||
import IndexView from "../../../../database/IndexView.svelte"
|
||||
import ModelDataTable from "../../../../database/ModelDataTable"
|
||||
import ActionsHeader from "../../../../database/ActionsHeader.svelte"
|
||||
import { store, backendUiStore } from "../../../../builderStore"
|
||||
import getIcon from "../../../../common/icon"
|
||||
import DropdownButton from "../../../../common/DropdownButton.svelte"
|
||||
import ActionButton from "../../../../common/ActionButton.svelte"
|
||||
import Modal from "../../../../common/Modal.svelte"
|
||||
import {
|
||||
CreateEditRecordModal,
|
||||
CreateEditModelModal,
|
||||
CreateEditViewModal,
|
||||
CreateDatabaseModal,
|
||||
DeleteRecordModal,
|
||||
CreateUserModal,
|
||||
} from "../../../../database/ModelDataTable/modals"
|
||||
|
||||
let selectedRecord
|
||||
|
||||
function selectRecord(record) {
|
||||
selectedRecord = record
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
backendUiStore.actions.modals.hide()
|
||||
}
|
||||
|
||||
$: recordOpen = $backendUiStore.visibleModal === "RECORD"
|
||||
$: modelOpen = $backendUiStore.visibleModal === "MODEL"
|
||||
$: viewOpen = $backendUiStore.visibleModal === "VIEW"
|
||||
$: databaseOpen = $backendUiStore.visibleModal === "DATABASE"
|
||||
$: deleteRecordOpen = $backendUiStore.visibleModal === "DELETE_RECORD"
|
||||
$: userOpen = $backendUiStore.visibleModal === "USER"
|
||||
$: breadcrumbs = $backendUiStore.breadcrumbs.join(" / ")
|
||||
</script>
|
||||
|
||||
<Modal isOpen={!!$backendUiStore.visibleModal} {onClosed}>
|
||||
{#if recordOpen}
|
||||
<CreateEditRecordModal record={selectedRecord} {onClosed} />
|
||||
{/if}
|
||||
{#if modelOpen}
|
||||
<CreateEditModelModal {onClosed} />
|
||||
{/if}
|
||||
{#if viewOpen}
|
||||
<CreateEditViewModal {onClosed} />
|
||||
{/if}
|
||||
{#if databaseOpen}
|
||||
<CreateDatabaseModal {onClosed} />
|
||||
{/if}
|
||||
{#if deleteRecordOpen}
|
||||
<DeleteRecordModal record={selectedRecord} {onClosed} />
|
||||
{/if}
|
||||
{#if userOpen}
|
||||
<CreateUserModal {onClosed} />
|
||||
{/if}
|
||||
</Modal>
|
||||
|
||||
<div class="root">
|
||||
<div class="node-view">
|
||||
<div class="database-actions">
|
||||
<div class="budibase__label--big">{breadcrumbs}</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ActionButton
|
||||
primary
|
||||
on:click={() => {
|
||||
selectedRecord = null
|
||||
backendUiStore.actions.modals.show('RECORD')
|
||||
}}>
|
||||
Create new record
|
||||
</ActionButton>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $backendUiStore.selectedDatabase.id}
|
||||
<ModelDataTable {selectRecord} />
|
||||
{:else}Please select a database{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.node-view {
|
||||
overflow-y: auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.database-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -1 +0,0 @@
|
|||
<slot />
|
|
@ -1,2 +0,0 @@
|
|||
<!-- routify:options $index=false -->
|
||||
<slot />
|
|
@ -1,10 +1,8 @@
|
|||
<script>
|
||||
import AppList from "../AppList.svelte"
|
||||
import PackageRoot from "../PackageRoot.svelte"
|
||||
import Settings from "../Settings.svelte"
|
||||
import AppList from "components/start/AppList.svelte"
|
||||
import { onMount } from "svelte"
|
||||
import IconButton from "../common/IconButton.svelte"
|
||||
import Spinner from "../common/Spinner.svelte"
|
||||
import IconButton from "components/common/IconButton.svelte"
|
||||
import Spinner from "components/common/Spinner.svelte"
|
||||
|
||||
let promise = getApps()
|
||||
|
||||
|
@ -21,7 +19,6 @@
|
|||
</script>
|
||||
|
||||
<main>
|
||||
|
||||
{#await promise}
|
||||
<div class="spinner-container">
|
||||
<Spinner />
|
||||
|
|
|
@ -3,6 +3,7 @@ const StatusCodes = require("../../utilities/statusCodes")
|
|||
const {
|
||||
getPackageForBuilder,
|
||||
getApps,
|
||||
saveBackend
|
||||
} = require("../../utilities/builder")
|
||||
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ run the budibase server and builder in dev mode (i.e. with hot reloading):
|
|||
|
||||
1. Open a new console
|
||||
2. `yarn dev` (from root)
|
||||
3. Access the builder on http://localhost:3000
|
||||
3. Access the builder on http://localhost:4001/_builder/
|
||||
|
||||
This will enable watch mode for both the client AND the server.
|
||||
|
||||
|
|
Loading…
Reference in New Issue