development setup, adding data components

This commit is contained in:
Martin McKeaveney 2020-05-06 10:33:30 +01:00
parent 2afd1cd4dd
commit 392de2efcc
48 changed files with 364 additions and 7915 deletions

7563
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
"publishdev": "lerna run publishdev",
"publishnpm": "yarn build && lerna publish",
"clean": "lerna clean",
"dev": "lerna run --parallel --stream dev:builder",
"dev": "./scripts/symlinkDev.js && lerna run --parallel --stream dev:builder",
"test": "lerna run test",
"lint": "eslint packages",
"lint:fix": "eslint --fix packages",

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -151,7 +151,7 @@ export default {
targets: [
{ src: "src/index.html", dest: outputpath },
{ src: "src/favicon.png", dest: outputpath },
{ src: "src/assets", dest: outputpath },
{ src: "assets", dest: outputpath },
{
src: "node_modules/@budibase/client/dist/budibase-client.esm.mjs",
dest: outputpath,

View File

@ -161,10 +161,6 @@
text-transform: capitalize;
}
.select {
background: white;
}
table {
border: 1px solid #ccc;
background: #fff;

View File

@ -1,7 +1,7 @@
import api from "builderStore/api"
export async function createUser(user, instanceId) {
const CREATE_USER_URL = `/api/${instanceId}/users`
export async function createUser(user, appId, instanceId) {
const CREATE_USER_URL = `/api/${appId}/${instanceId}/users`
const response = await api.post(CREATE_USER_URL, user)
const json = await response.json()
return json.user;
@ -21,11 +21,11 @@ export async function deleteRecord(record, instanceId) {
return response
}
export async function loadRecord(key, { appname, instanceId }) {
const LOAD_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record${key}`
const response = await api.get(LOAD_RECORDS_URL)
return await response.json()
}
// export async function loadRecord(key, { appname, instanceId }) {
// const LOAD_RECORDS_URL = `/_builder/instance/${appname}/${instanceId}/api/record${key}`
// const response = await api.get(LOAD_RECORDS_URL)
// return await response.json()
// }
export async function saveRecord(record, instanceId) {
const SAVE_RECORDS_URL = `/api/${instanceId}/records`

View File

@ -10,10 +10,11 @@
$: valid = username && password
$: instanceId = $backendUiStore.selectedDatabase.id
$: appId = $store.appId
async function createUser() {
const user = { name: username, username, password }
const response = await api.createUser(user, instanceId);
const response = await api.createUser(user, appId, instanceId);
backendUiStore.actions.users.create(response)
onClosed()
}

View File

@ -4,8 +4,8 @@
import iframeTemplate from "./iframeTemplate";
import { pipe } from "components/common/core"
let iframe
let styles = ""
function transform_component(comp) {
const props = comp.props || comp
@ -24,7 +24,15 @@
)
)
$: hasComponent = !!$store.currentPreviewItem
$: styles = hasComponent ? $store.currentPreviewItem._css : ""
$: {
// Apply the CSS from the currently selected page and its screens
const currentPage = $store.pages[$store.currentPageName];
styles += currentPage._css;
for (let screen of currentPage._screens) {
styles += screen._css;
}
styles = styles
}
$: stylesheetLinks = pipe(
$store.pages.stylesheets,
@ -67,7 +75,7 @@
]
}
}],
appRootPath: `/_builder/instance/${$store.appname}/${$backendUiStore.selectedDatabase.id}/`,
appRootPath: `/`,
}
$: selectedComponentId = $store.currentComponentInfo ? $store.currentComponentInfo._id : ""

View File

@ -34,7 +34,7 @@
const onStyleChanged = store.setComponentStyle
function getProps(obj, keys) {
return keys.map((k, i) => [k, obj[k], obj.props._id + i])
return keys.map((key, i) => [key, obj[key], obj.props._id + i])
}
</script>

View File

@ -24,17 +24,18 @@
<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">
{#if prop_def !== "event"}
<div class="prop-container">
<PropControl
{setProp}
{prop_name}
prop_value={component[prop_name]}
prop_definition={prop_def}
{index}
disabled={false} />
<PropControl
{setProp}
{prop_name}
prop_value={component[prop_name]}
prop_definition={prop_def}
{index}
disabled={false} />
</div>
</div>
{/if}
{/each}
{/if}
</form>

View File

@ -166,7 +166,23 @@ export default {
name: "@budibase/materialdesign-components/recordForm",
},
children: []
}
},
{
_component: "@budibase/standard-components/datatable",
name: 'DataTable',
description: 'A table for displaying data from the backend.',
icon: 'ri-archive-drawer-fill',
commonProps: {},
children: []
},
{
_component: "@budibase/standard-components/dataform",
name: 'DataForm',
description: 'Form stuff',
icon: 'ri-file-edit-fill',
commonProps: {},
children: []
},
]
},
]

View File

@ -7,7 +7,7 @@
"scripts": {
"build": "rollup -c",
"test": "jest",
"publishdev": "yarn build && node ./scripts/publishDev.js"
"dev:builder": "rollup -cw"
},
"jest": {
"globals": {

View File

@ -102,7 +102,6 @@ export default {
}),
builtins(),
nodeglobals(),
//terser()
],
watch: {
clearScreen: false,

View File

@ -1,56 +0,0 @@
const { readdir, stat, copyFile, ensureDir } = require("fs-extra")
const { constants } = require("fs")
const { join, basename } = require("path")
const packagesFolder = ".."
const jsFile = dir => join(dir, "budibase-client.js")
const jsMapFile = dir => join(dir, "budibase-client.js.map")
const sourceJs = jsFile("dist")
const sourceJsMap = jsMapFile("dist")
const appPackages = join(
packagesFolder,
"server",
serverConfig.latestPackagesFolder
)
const publicMain = appName => join(appPackages, appName, "public", "main")
const publicUnauth = appName =>
join(appPackages, appName, "public", "unauthenticated")
;(async () => {
const apps = await readdir(appPackages)
const copySource = file => async toDir => {
await ensureDir(toDir)
const dest = join(toDir, basename(file))
try {
await copyFile(file, dest, constants.COPYFILE_FICLONE)
console.log(`COPIED ${file} to ${dest}`)
} catch (e) {
console.log(`COPY FAILED ${file} to ${dest}: ${e}`)
}
}
const copySourceJs = copySource(sourceJs)
const copySourceJsMap = copySource(sourceJsMap)
for (let app of apps) {
if (app === ".data") continue
if (!(await stat(join(appPackages, app))).isDirectory()) continue
//await copySourceJs(nodeModules(app))
//await copySourceJsMap(nodeModules(app))
await copySourceJs(publicMain(app))
await copySourceJsMap(publicMain(app))
await copySourceJs(publicUnauth(app))
await copySourceJsMap(publicUnauth(app))
await copySource(join("dist", "budibase-client.esm.mjs"))(
join(packagesFolder, "server", "builder")
)
}
})()

View File

@ -1,8 +1,8 @@
import { createApp } from "./createApp"
import { trimSlash } from "./common/trimSlash"
import { builtins, builtinLibName } from "./render/builtinComponents"
import * as standardComponents from "@budibase/standard-components";
import * as materialDesignComponents from "@budibase/materialdesign-components";
import * as standardComponents from "../../standard-components/dist";
import * as materialDesignComponents from "../../materialdesign-components/dist";
/**
* create a web application from static budibase definition files.

View File

@ -9,7 +9,7 @@
"testbuild": "rollup -w -c rollup.testconfig.js",
"dev": "run-p start:dev testbuild",
"start:dev": "sirv public --single --dev",
"publishdev": "yarn build && node ./scripts/publishDev.js"
"dev:builder": "rollup -cw"
},
"devDependencies": {
"@budibase/client": "^0.0.32",

View File

@ -1,94 +0,0 @@
const { readdir, stat, copyFile, ensureDir } = require("fs-extra")
const { constants } = require("fs")
const { join, basename } = require("path")
const serverConfig = require("../../server/config")()
const packagesFolder = ".."
const jsFile = dir => join(dir, "index.js")
const jsMapFile = dir => join(dir, "index.js.map")
const sourceJs = jsFile("dist")
const sourceJsMap = jsMapFile("dist")
const componentsFile = "components.json"
const appPackages = join(
packagesFolder,
"server",
serverConfig.latestPackagesFolder
)
const publicMain = appName =>
join(
appPackages,
appName,
"public",
"main",
"lib",
"node_modules",
"@budibase",
"materialdesign-components"
)
const publicUnauth = appName =>
join(
appPackages,
appName,
"public",
"unauthenticated",
"lib",
"node_modules",
"@budibase",
"materialdesign-components"
)
const nodeModulesDist = appName =>
join(
appPackages,
appName,
"node_modules",
"@budibase",
"materialdesign-components",
"dist"
)
const nodeModules = appName =>
join(
appPackages,
appName,
"node_modules",
"@budibase",
"materialdesign-components"
)
;(async () => {
const apps = await readdir(appPackages)
const copySource = file => async toDir => {
await ensureDir(toDir)
const dest = join(toDir, basename(file))
try {
await copyFile(file, dest, constants.COPYFILE_FICLONE)
console.log(`COPIED ${file} to ${dest}`)
} catch (e) {
console.log(`COPY FAILED ${file} to ${dest}: ${e}`)
}
}
const copySourceJs = copySource(sourceJs)
const copySourceJsMap = copySource(sourceJsMap)
const copyComponentsJson = copySource(componentsFile)
for (let app of apps) {
if (app === ".data") continue
if (!(await stat(join(appPackages, app))).isDirectory()) continue
await copySourceJs(nodeModulesDist(app))
await copySourceJsMap(nodeModulesDist(app))
await copyComponentsJson(nodeModules(app))
await copySourceJs(join(publicMain(app), "dist"))
await copySourceJsMap(join(publicMain(app), "dist"))
await copySourceJs(join(publicUnauth(app), "dist"))
await copySourceJsMap(join(publicUnauth(app), "dist"))
}
})()

View File

@ -8,24 +8,43 @@ exports.authenticate = async ctx => {
if (!username) ctx.throw(400, "Username Required.");
if (!password) ctx.throw(400, "Password Required");
// query couch for their username
const db = new CouchDB(ctx.params.clientId);
const dbUser = await db.query("by_username", {
// TODO: Don't use this. It can't be relied on
const referer = ctx.request.headers.referer.split("/");
const appId = referer[3];
// find the instance that the user is associated with
const db = new CouchDB(`client-${process.env.CLIENT_ID}`);
const app = await db.get(appId);
const instanceId = app.userInstanceMap[username];
if (!instanceId) ctx.throw(500, "User is not associated with an instance of app", appId)
// Check the user exists in the instance DB by username
const instanceDb = new CouchDB(instanceId);
const { rows } = await instanceDb.query("database/by_username", {
include_docs: true,
key: username
username
});
if (rows.length === 0) ctx.throw(500, `User does not exist.`);
const dbUser = rows[0].doc;
// authenticate
if (await bcrypt.compare(password, dbUser.password)) {
const payload = {
userId: dbUser._id,
accessLevel: "",
instanceId: ctx.params.instanceId
instanceId: instanceId
};
const token = jwt.sign(payload, ctx.config.secret, {
expiresIn: "1 day"
});
ctx.body = token;
ctx.body = {
token,
...dbUser
};
} else {
ctx.throw(401, "Invalid credentials.");
}

View File

@ -2,12 +2,20 @@ const CouchDB = require("../../db");
const { homedir } = require("os");
const { resolve, join } = require("path");
const isDev = process.env.NODE_ENV !== "production";
exports.fetchAppComponentDefinitions = async function(ctx) {
const db = new CouchDB(`client-${ctx.params.clientId}`);
const app = await db.get(ctx.params.appId)
const componentDefinitions = app.componentLibraries.reduce((acc, componentLibrary) => {
const appDirectory = resolve(homedir(), ".budibase", ctx.params.appId, "node_modules");
let appDirectory = resolve(homedir(), ".budibase", ctx.params.appId, "node_modules");
if (isDev) {
appDirectory = "/tmp/.budibase";
}
const componentJson = require(join(appDirectory, componentLibrary, "components.json"));
const result = {};

View File

@ -2,7 +2,6 @@ const CouchDB = require("../../db");
exports.create = async function(ctx) {
const instanceName = ctx.request.body.name;
// await couchdb.db.create(instanceName);
const { clientId, applicationId } = ctx.params;
const db = new CouchDB(instanceName);
@ -13,6 +12,13 @@ exports.create = async function(ctx) {
applicationId
},
views: {
by_username: {
map: function(doc) {
if (doc.type === "user") {
emit([doc.username], doc._id);
}
}.toString()
},
by_type: {
map: function(doc) {
emit([doc.type], doc._id);

View File

@ -1,13 +1,19 @@
const send = require("koa-send");
const { resolve } = require("path")
const { resolve, join } = require("path")
const { homedir } = require("os");
const isProduction = process.env.NODE_ENV === "production";
exports.serveBuilder = async function(ctx) {
const builderPath = resolve(process.cwd(), "builder")
await send(ctx, ctx.file, { root: builderPath })
}
exports.serveApp = async function(ctx) {
// ONLY RELEVANT FOR THE CLIENT LIB
// const devPath = join("/tmp", ".budibase", ctx.params.appId);
// TODO: update homedir stuff to wherever budi is run
// default to homedir
const appPath = resolve(
@ -18,38 +24,30 @@ exports.serveApp = async function(ctx) {
ctx.isAuthenticated ? "main" : "unauthenticated"
);
// TODO: Hook up to JWT auth in real app
// TODO: serve CSS and other assets
// resolve main page if user authenticated
await send(ctx, ctx.file, { root: appPath })
}
exports.serveComponentLibrary = async function(ctx) {
// TODO: update homedir stuff to wherever budi is run
// default to homedir
const componentLibraryPath = resolve(
homedir(),
".budibase",
ctx.params.appId,
"node_modules",
let componentLibraryPath = join(
"/tmp",
".budibase",
decodeURI(ctx.query.library),
"dist"
);
if (isProduction) {
// TODO: update homedir stuff to wherever budi is run
// default to homedir
componentLibraryPath = resolve(
homedir(),
".budibase",
ctx.params.appId,
"node_modules",
decodeURI(ctx.query.library),
"dist"
);
}
await send(ctx, "/index.js", { root: componentLibraryPath })
}
exports.serveComponentDefinitions = async function(ctx) {
// TODO: update homedir stuff to wherever budi is run
// default to homedir
const componentLibraryPath = resolve(
homedir(),
".budibase",
ctx.params.appId,
"node_modules",
decodeURI(ctx.query.library),
"dist"
);
await send(ctx, "/index.js", { root: componentLibraryPath })
}

View File

@ -25,12 +25,12 @@ exports.create = async function(ctx) {
});
// the clientDB needs to store a map of users against the app
const clientDB = new CouchDB(process.env.CLIENT_ID);
const app = await clientDB.get(ctx.params.appId);
const clientDb = new CouchDB(`client-${process.env.CLIENT_ID}`);
const app = await clientDb.get(ctx.params.appId);
app.userInstanceMap = {
...app.userInstanceMap,
[response._id]: ctx.params.instanceId
[username]: ctx.params.instanceId
}
await clientDb.put(app);

View File

@ -10,7 +10,7 @@ const controller = {
const response = [];
for (let name in designDoc.views) {
if (!name.startsWith("all") && name !== "by_type") {
if (!name.startsWith("all") && name !== "by_type" && name !== "by_username") {
response.push({
name,
...designDoc.views[name]

View File

@ -36,7 +36,10 @@ module.exports = app => {
// TODO: temp dev middleware
// ctx.sessionId = ctx.session._sessCtx.externalKey
// ctx.session.accessed = true
ctx.config = { latestPackagesFolder: resolve(homedir(), ".budibase") }
ctx.config = {
latestPackagesFolder: resolve(homedir(), ".budibase"),
secret: "foo"
}
await next();
});

View File

@ -11,30 +11,30 @@ const {
const router = Router()
router.post("/_builder/api/:appname/pages/:pageName", async ctx => {
router.post("/_builder/api/:appId/pages/:pageName", async ctx => {
await buildPage(
ctx.config,
ctx.params.appname,
ctx.params.appId,
ctx.params.pageName,
ctx.request.body
)
ctx.response.status = StatusCodes.OK
})
router.get("/_builder/api/:appname/pages/:pagename/screens", async ctx => {
router.get("/_builder/api/:appId/pages/:pagename/screens", async ctx => {
ctx.body = await listScreens(
ctx.config,
ctx.params.appname,
ctx.params.appId,
ctx.params.pagename
)
ctx.response.status = StatusCodes.OK
})
router
.post("/_builder/api/:appname/pages/:pagename/screen", async ctx => {
.post("/_builder/api/:appId/pages/:pagename/screen", async ctx => {
ctx.body = await saveScreen(
ctx.config,
ctx.params.appname,
ctx.params.appId,
ctx.params.pagename,
ctx.request.body
)

View File

@ -6,7 +6,7 @@ const {
readFile,
writeJSON,
} = require("fs-extra")
const { join, resolve, dirname } = require("path")
const { join, resolve } = require("path")
const sqrl = require("squirrelly")
const { convertCssToFiles } = require("./convertCssToFiles")
const publicPath = require("./publicPath")

View File

@ -4977,9 +4977,9 @@ supports-color@^7.1.0:
has-flag "^4.0.0"
svelte@^3.9.2:
version "3.21.0"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.21.0.tgz#e326591cb92267e90b4fb5b961d80d9763792551"
integrity sha512-smh3LZKPCGJ+UXa0iZvUmuDctPYCwPY1opmClTWTm+l6e4y9FHLoCZMiue8YIeyc9JvlGT/EK0xry0diXjFDZQ==
version "3.22.2"
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.22.2.tgz#06585244191bf7a112af2a0025610f33d77c3715"
integrity sha512-DxumO0+vvHA6NSc2jtVty08I8lFI43q8P2zX6JxZL8J1kqK5NVjad6TRM/twhnWXC+QScnwkZ15O6X1aTsEKTA==
symbol-tree@^3.2.2:
version "3.2.4"

View File

@ -262,6 +262,29 @@
"color": "colour"
}
},
"datatable": {
"description": "Other thingwy",
"props": {
"_viewName": "string",
"_instanceId": "string",
"model": {
"store": true,
"type": "options",
"default": "",
"options": [
"something",
"something"
]
}
}
},
"dataform": {
"description": "an HTML table that fetches data from a model or view and displays it.",
"props": {
"_viewName": "string",
"_instanceId": "string"
}
},
"link": {
"description": "an HTML anchor <a> tag",
"props": {
@ -402,4 +425,4 @@
"className": "string"
}
}
}
}

View File

@ -9,7 +9,7 @@
"testbuild": "rollup -w -c rollup.testconfig.js",
"dev": "run-p start:dev testbuild",
"start:dev": "sirv public --single --dev",
"publishdev": "yarn build && node ./scripts/publishDev.js"
"dev:builder": "rollup -cw"
},
"devDependencies": {
"@budibase/client": "^0.0.32",

View File

@ -1,88 +0,0 @@
const { readdir, stat, copyFile, ensureDir } = require("fs-extra")
const { constants } = require("fs")
const { join, basename } = require("path")
const serverConfig = require("../../server/config")()
const packagesFolder = ".."
const jsFile = dir => join(dir, "index.js")
const jsMapFile = dir => join(dir, "index.js.map")
const sourceJs = jsFile("dist")
const sourceJsMap = jsMapFile("dist")
const componentsFile = "components.json"
const appPackages = join(
packagesFolder,
"server",
resolve(homedir(), ".budibase")
)
const publicMain = appName =>
join(
appPackages,
appName,
"public",
"main",
"lib",
"node_modules",
"@budibase",
"standard-components"
)
const publicUnauth = appName =>
join(
appPackages,
appName,
"public",
"unauthenticated",
"lib",
"node_modules",
"@budibase",
"standard-components"
)
const nodeModulesDist = appName =>
join(
appPackages,
appName,
"node_modules",
"@budibase",
"standard-components",
"dist"
)
const nodeModules = appName =>
join(appPackages, appName, "node_modules", "@budibase", "standard-components")
;(async () => {
const apps = await readdir(appPackages)
const copySource = file => async toDir => {
await ensureDir(toDir)
const dest = join(toDir, basename(file))
try {
await copyFile(file, dest, constants.COPYFILE_FICLONE)
console.log(`COPIED ${file} to ${dest}`)
} catch (e) {
console.log(`COPY FAILED ${file} to ${dest}: ${e}`)
}
}
const copySourceJs = copySource(sourceJs)
const copySourceJsMap = copySource(sourceJsMap)
const copyComponentsJson = copySource(componentsFile)
for (let app of apps) {
if (app === ".data") continue
if (!(await stat(join(appPackages, app))).isDirectory()) continue
await copySourceJs(nodeModulesDist(app))
await copySourceJsMap(nodeModulesDist(app))
await copyComponentsJson(nodeModules(app))
await copySourceJs(join(publicMain(app), "dist"))
await copySourceJsMap(join(publicMain(app), "dist"))
await copySourceJs(join(publicUnauth(app), "dist"))
await copySourceJsMap(join(publicUnauth(app), "dist"))
}
})()

View File

@ -0,0 +1,27 @@
<script>
import { onMount } from "svelte";
export let _bb
export let _viewName
export let _instanceId
let username
let password
</script>
<form class="uk-form">
<div>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Username</label>
<input class="uk-input" type="text" bind:value={username} />
</div>
<div class="uk-margin">
<label class="uk-form-label" for="form-stacked-text">Password</label>
<input class="uk-input" type="password" bind:value={password} />
</div>
</div>
</form>
<style>
</style>

View File

@ -0,0 +1,110 @@
<script>
import { onMount } from "svelte";
// import { cssVars, createClasses } from "./cssVars"
// import { buildStyle } from "./buildStyle"
export let _bb
export let onLoad
export let _viewName
export let _instanceId
let cssVariables
let headers = []
let data = []
async function fetchData() {
const FETCH_RECORDS_URL = `/api/${_instanceId}/${_viewName}/records`;
const response = await _bb.api.get(FETCH_RECORDS_URL);
if (response.status === 200) {
const json = await response.json();
if (json.length > 0) {
data = json;
headers = Object.keys(data[0]);
} else {
console.log("NO DATA");
}
} else {
throw new Error("Failed to fetch records..");
console.log("FAILED");
}
}
onMount(async () => {
await fetchData();
})
</script>
<!-- This prop was in the old one -->
<!-- use:cssVars={cssVariables} -->
<table class="uk-table">
<thead>
<tr>
{#each headers as header}
<th>{header}</th>
{/each}
</tr>
</thead>
<tbody>
{#each data as row}
<tr>
{#each headers as header}
{#if row[header]}
<td>
{row[header]}
</td>
{/if}
{/each}
</tr>
{/each}
</tbody>
</table>
<!-- <button
bind:this={theButton}
use:cssVars={cssVariables}
class="{className}
{customClasses}"
disabled={disabled || false}
on:click={clickHandler}
style={buttonStyles}>
{#if !_bb.props._children || _bb.props._children.length === 0}
{contentText}
{/if}
</button> -->
<style>
table {
border: 1px solid #ccc;
background: #fff;
border-radius: 3px;
border-collapse: collapse;
}
thead {
background: #f9f9f9;
border: 1px solid #ccc;
}
thead th {
color: var(--button-text);
text-transform: capitalize;
font-weight: 500;
font-size: 14px;
text-rendering: optimizeLegibility;
letter-spacing: 1px;
}
tbody tr {
border-bottom: 1px solid #ccc;
transition: 0.3s background-color;
color: var(--secondary100);
font-size: 14px;
}
tbody tr:hover {
background: #fafafa;
}
</style>

View File

@ -4,7 +4,6 @@
export let usernameLabel = "Username"
export let passwordLabel = "Password"
export let loginButtonLabel = "Login"
export let loginRedirect = ""
export let logo = ""
export let buttonClass = ""
export let inputClass = ""
@ -13,8 +12,8 @@
let username = ""
let password = ""
let busy = false
let incorrect = false
let loading = false
let error = false
let _logo = ""
let _buttonClass = ""
let _inputClass = ""
@ -25,32 +24,25 @@
_inputClass = inputClass || "default-input"
}
const login = () => {
busy = true
_bb.api
.post("/api/authenticate", { username, password })
.then(r => {
busy = false
if (r.status === 200) {
return r.json()
} else {
incorrect = true
return
}
})
.then(user => {
if (user) {
localStorage.setItem("budibase:user", JSON.stringify(user))
location.reload()
}
})
const login = async () => {
loading = true
const response = _bb.api.post("/api/authenticate", { username, password });
if (response.status === 200) {
const json = await response.json();
localStorage.setItem("budibase:token", json.token);
// TODO: possibly do something with the user information in the response?
location.reload()
} else {
loading = false
error = true
}
}
</script>
<div class="root">
<div class="content">
{#if _logo}
<div class="logo-container">
<img src={_logo} alt="logo" />
@ -69,17 +61,15 @@
</div>
<div class="login-button-container">
<button disabled={busy} on:click={login} class={_buttonClass}>
<button disabled={loading} on:click={login} class={_buttonClass}>
{loginButtonLabel}
</button>
</div>
{#if incorrect}
{#if error}
<div class="incorrect-details-panel">Incorrect username or password</div>
{/if}
</div>
</div>
<style>

View File

@ -14,3 +14,5 @@ export { default as link } from "./Link.svelte"
export { default as image } from "./Image.svelte"
export { default as icon } from "./Icon.svelte"
export { default as Navigation } from "./Navigation.svelte"
export { default as datatable } from "./DataTable.svelte"
export { default as dataform } from "./DataForm.svelte"

43
scripts/symlinkDev.js Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env node
/**
This script symlinks the budibase component and client paths to the
ones that exist in your local development directories. This means you
can work your budibase apps but also change code for the components
and client library in real time.
*/
const fs = require("fs");
const { resolve } = require("path")
const devDir = "/tmp/.budibase/@budibase";
// create the dev directory if it doesn't exist
if (!fs.existsSync(devDir)) {
fs.mkdirSync(devDir, { recursive: true });
}
const SYMLINK_PATHS = [
{
symlink: "/tmp/.budibase/@budibase/materialdesign-components",
destination: resolve("packages/materialdesign-components"),
},
{
symlink: "/tmp/.budibase/@budibase/standard-components",
destination: resolve("packages/standard-components")
},
{
symlink: "/tmp/.budibase/budibase-client.esm.mjs",
destination: resolve("packages/client/dist/budibase-client.esm.mjs")
},
{
symlink: "/tmp/.budibase/budibase-client.js",
destination: resolve("packages/client/dist/budibase-client.js"),
}
]
SYMLINK_PATHS.forEach(sym => {
fs.symlinkSync(sym.destination, sym.symlink);
});
console.log("Dev Symlinks Created Successfully.")