Merge branch 'master' of https://github.com/Budibase/budibase into britecharts/separate-components-2
This commit is contained in:
commit
fe70bb16d6
|
@ -33,3 +33,4 @@ jobs:
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
name: Budibase CI
|
name: Budibase CI
|
||||||
|
- run: yarn test:e2e:ci
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -19,14 +19,15 @@
|
||||||
"build": "lerna run build",
|
"build": "lerna run build",
|
||||||
"initialise": "lerna run initialise",
|
"initialise": "lerna run initialise",
|
||||||
"publishdev": "lerna run publishdev",
|
"publishdev": "lerna run publishdev",
|
||||||
"publishnpm": "yarn build && lerna publish",
|
"publishnpm": "yarn build && lerna publish --force-publish",
|
||||||
"clean": "lerna clean",
|
"clean": "lerna clean",
|
||||||
"dev": "node ./scripts/symlinkDev.js && lerna run --parallel --stream dev:builder",
|
"dev": "node ./scripts/symlinkDev.js && lerna run --parallel --stream dev:builder",
|
||||||
"test": "lerna run test",
|
"test": "lerna run test",
|
||||||
"lint": "eslint packages",
|
"lint": "eslint packages",
|
||||||
"lint:fix": "eslint --fix packages",
|
"lint:fix": "eslint --fix packages",
|
||||||
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx,svelte}\"",
|
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx,svelte}\"",
|
||||||
"test:e2e": "lerna run cy:test"
|
"test:e2e": "lerna run cy:test",
|
||||||
|
"test:e2e:ci": "lerna run cy:ci"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/icon-button": "4.0.0",
|
"@material/icon-button": "4.0.0",
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
/node_modules/
|
/node_modules/
|
||||||
node_modules_win
|
node_modules_win
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
|
||||||
release/
|
release/
|
||||||
dist/
|
dist/
|
||||||
cypress/screenshots
|
cypress/screenshots
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"baseUrl": "http://localhost:4001/_builder/",
|
"baseUrl": "http://localhost:4001/_builder/",
|
||||||
"video": false
|
"video": true,
|
||||||
|
"projectId": "bmbemn"
|
||||||
}
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"budibase": "CB373643-3FC4-4902-9E31-449C0ED066B6"
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
context('Create an Application', () => {
|
context('Create an Application', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
cy.server()
|
||||||
cy.visit('localhost:4001/_builder')
|
cy.visit('localhost:4001/_builder')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
context('Create Components', () => {
|
xcontext('Create Components', () => {
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
cy.server()
|
||||||
cy.visit('localhost:4001/_builder')
|
cy.visit('localhost:4001/_builder')
|
||||||
// https://on.cypress.io/type
|
// https://on.cypress.io/type
|
||||||
cy.createApp('Model App', 'Model App Description')
|
cy.createApp('Model App', 'Model App Description')
|
||||||
cy.createModel('dog', 'name', 'age')
|
cy.createTable('dog', 'name', 'age')
|
||||||
cy.addRecord('bob', '15')
|
cy.addRecord('bob', '15')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -22,8 +23,8 @@ context('Create Components', () => {
|
||||||
})
|
})
|
||||||
it('change the font size of the headline', () => {
|
it('change the font size of the headline', () => {
|
||||||
cy.contains('Typography').click()
|
cy.contains('Typography').click()
|
||||||
cy.get('input[name="font-size"]')
|
cy.get('[data-cy=font-size-prop-control]').click()
|
||||||
.type('60px')
|
cy.contains("60px").click()
|
||||||
cy.contains('Design').click()
|
cy.contains('Design').click()
|
||||||
|
|
||||||
getIframeBody().contains('An Amazing headline!').should('have.css', 'font-size', '60px')
|
getIframeBody().contains('An Amazing headline!').should('have.css', 'font-size', '60px')
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
context('Create a Model', () => {
|
|
||||||
|
|
||||||
before(() => {
|
|
||||||
cy.visit('localhost:4001/_builder')
|
|
||||||
// https://on.cypress.io/type
|
|
||||||
cy.createApp('Model App', 'Model App Description')
|
|
||||||
})
|
|
||||||
|
|
||||||
// https://on.cypress.io/interacting-with-elements
|
|
||||||
it('should create a new model', () => {
|
|
||||||
|
|
||||||
cy.createModel('dog', 'name', 'age')
|
|
||||||
|
|
||||||
// Check if model exists
|
|
||||||
cy.get('.title').should('have.text', 'dog')
|
|
||||||
})
|
|
||||||
it('should add a record', () => {
|
|
||||||
cy.addRecord('bob', '15')
|
|
||||||
|
|
||||||
cy.contains('bob').should('have.text', 'bob')
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
context('Create a Table', () => {
|
||||||
|
before(() => {
|
||||||
|
cy.visit('localhost:4001/_builder')
|
||||||
|
cy.createApp('Table App', 'Table App Description')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a new Table', () => {
|
||||||
|
cy.createTable('dog')
|
||||||
|
|
||||||
|
// Check if Table exists
|
||||||
|
cy.get('.title').should('have.text', 'dog')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('adds a new column to the table', () => {
|
||||||
|
cy.addColumn('dog', 'name', 'Plain Text')
|
||||||
|
|
||||||
|
cy.contains('name').should("be.visible")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('creates a record in the table', () => {
|
||||||
|
cy.addRecord(["Rover"])
|
||||||
|
|
||||||
|
cy.contains('Rover').should("be.visible")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates a column on the table', () => {
|
||||||
|
cy.contains("name").click()
|
||||||
|
cy.get("[data-cy='edit-column-header']").click()
|
||||||
|
|
||||||
|
cy.get("[placeholder=Name]").type("updated")
|
||||||
|
cy.get("select").select("Plain Text")
|
||||||
|
|
||||||
|
cy.contains("Save Column").click()
|
||||||
|
|
||||||
|
cy.contains('nameupdated').should('have.text', 'nameupdated ')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('edits a record', () => {
|
||||||
|
cy.get("tbody .ri-more-line").click()
|
||||||
|
cy.get("[data-cy=edit-row]").click()
|
||||||
|
cy.get(".actions input").type("updatedRecord")
|
||||||
|
cy.contains("Save").click()
|
||||||
|
|
||||||
|
cy.contains('updatedRecord').should('have.text', 'updatedRecord')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deletes a record', () => {
|
||||||
|
cy.get("tbody .ri-more-line").click()
|
||||||
|
cy.get("[data-cy=delete-row]").click()
|
||||||
|
cy.get(".modal-actions").contains("Delete").click()
|
||||||
|
|
||||||
|
cy.contains('updatedRecord').should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deletes a column', () => {
|
||||||
|
cy.contains("name").click()
|
||||||
|
cy.get("[data-cy='delete-column-header']").click()
|
||||||
|
|
||||||
|
cy.contains('nameupdated').should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deletes a table', () => {
|
||||||
|
cy.contains("div", "dog").get(".ri-more-line").click()
|
||||||
|
cy.get("[data-cy=delete-table]").click()
|
||||||
|
cy.get(".modal-actions").contains("Delete").click()
|
||||||
|
|
||||||
|
cy.contains('dog').should('not.exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -1,6 +1,7 @@
|
||||||
context('Create a User', () => {
|
context('Create a User', () => {
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
cy.server()
|
||||||
cy.visit('localhost:4001/_builder')
|
cy.visit('localhost:4001/_builder')
|
||||||
// https://on.cypress.io/type
|
// https://on.cypress.io/type
|
||||||
cy.createApp('User App', 'This app is used to test user creation')
|
cy.createApp('User App', 'This app is used to test user creation')
|
||||||
|
@ -8,12 +9,9 @@ context('Create a User', () => {
|
||||||
|
|
||||||
// https://on.cypress.io/interacting-with-elements
|
// https://on.cypress.io/interacting-with-elements
|
||||||
it('should create a user', () => {
|
it('should create a user', () => {
|
||||||
// Close Model modal that shows up after creating an app
|
|
||||||
cy.get('.close').click()
|
|
||||||
|
|
||||||
cy.createUser('bbuser', 'test', 'ADMIN')
|
cy.createUser('bbuser', 'test', 'ADMIN')
|
||||||
|
|
||||||
// Check to make sure user was created!
|
// Check to make sure user was created!
|
||||||
cy.contains('bbuser').should('have.text', 'bbuser')
|
cy.get("input[disabled]").should('have.value', 'bbuser')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
context('Create a workflow', () => {
|
xcontext('Create a workflow', () => {
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
|
cy.server()
|
||||||
cy.visit('localhost:4001/_builder')
|
cy.visit('localhost:4001/_builder')
|
||||||
|
|
||||||
cy.createApp('Workflow Test App', 'This app is used to test that workflows do in fact work!')
|
cy.createApp('Workflow Test App', 'This app is used to test that workflows do in fact work!')
|
||||||
|
@ -8,12 +9,10 @@ context('Create a workflow', () => {
|
||||||
|
|
||||||
// https://on.cypress.io/interacting-with-elements
|
// https://on.cypress.io/interacting-with-elements
|
||||||
it('should create a workflow', () => {
|
it('should create a workflow', () => {
|
||||||
cy.createModel('dog', 'name', 'age')
|
cy.createTable('dog', 'name', 'age')
|
||||||
cy.createUser('bbuser', 'test', 'ADMIN')
|
|
||||||
|
|
||||||
|
|
||||||
cy.contains('workflow').click()
|
cy.contains('workflow').click()
|
||||||
cy.get('.new-workflow-button').click()
|
cy.contains('Create New Workflow').click()
|
||||||
cy.get('input').type('Add Record')
|
cy.get('input').type('Add Record')
|
||||||
cy.contains('Save').click()
|
cy.contains('Save').click()
|
||||||
|
|
||||||
|
@ -28,14 +27,13 @@ context('Create a workflow', () => {
|
||||||
cy.get(':nth-child(3) > .budibase__input').type('11')
|
cy.get(':nth-child(3) > .budibase__input').type('11')
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.get('[data-cy=save-workflow-setup]').click()
|
cy.contains('Save Workflow').click()
|
||||||
cy.get('.workflow-button').click()
|
|
||||||
|
|
||||||
// Activate Workflow
|
// Activate Workflow
|
||||||
cy.get('[data-cy=activate-workflow]').click()
|
cy.get('[data-cy=activate-workflow]').click()
|
||||||
|
|
||||||
})
|
})
|
||||||
it('should add record when a new record is added', () => {
|
xit('should add record when a new record is added', () => {
|
||||||
cy.contains('backend').click()
|
cy.contains('backend').click()
|
||||||
|
|
||||||
cy.addRecord('bob', '15')
|
cy.addRecord('bob', '15')
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
|
|
||||||
context('Screen Tests', () => {
|
context('Screen Tests', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
|
cy.server()
|
||||||
cy.visit('localhost:4001/_builder')
|
cy.visit('localhost:4001/_builder')
|
||||||
cy.createApp('Conor Cy App', 'Model App Description')
|
cy.createApp('Conor Cy App', 'Model App Description')
|
||||||
cy.navigateToFrontend()
|
cy.navigateToFrontend()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should successful create a screen', () => {
|
it('Should successful create a screen', () => {
|
||||||
cy.createScreen("test Screen")
|
cy.createScreen("test Screen", "/test")
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should rename a screen', () => {
|
it('Should rename a screen', () => {
|
||||||
|
|
|
@ -19,4 +19,5 @@
|
||||||
module.exports = (on, config) => {
|
module.exports = (on, config) => {
|
||||||
// `on` is used to hook into various events Cypress emits
|
// `on` is used to hook into various events Cypress emits
|
||||||
// `config` is the resolved Cypress config
|
// `config` is the resolved Cypress config
|
||||||
|
require("cypress-terminal-report/src/installLogsPrinter")(on)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
// What this script does:
|
// What this script does:
|
||||||
// 1. Removes the old test folder if it exists (.budibase-cypress)
|
// 1. Removes the old test folder if it exists (.budibase)
|
||||||
// 2. Initialises using `.budibase-cypress`
|
// 2. Initialises using `.budibase`
|
||||||
// 3. Runs the server using said folder
|
// 3. Runs the server using said folder
|
||||||
|
|
||||||
const rimraf = require("rimraf")
|
const rimraf = require("rimraf")
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
const homedir = join(require("os").homedir(), ".budibase-cypress")
|
|
||||||
const init = require("../../cli/src/commands/init/initHandler")
|
|
||||||
const run = require("../../cli/src/commands/run/runHandler")
|
const run = require("../../cli/src/commands/run/runHandler")
|
||||||
|
const initialiseBudibase = require("../../server/src/utilities/initialiseBudibase")
|
||||||
|
|
||||||
|
const homedir = join(require("os").homedir(), ".budibase")
|
||||||
|
|
||||||
rimraf.sync(homedir)
|
rimraf.sync(homedir)
|
||||||
|
|
||||||
init({ dir: homedir, clientId: "cypress-test" }).then(() => {
|
process.env.BUDIBASE_API_KEY = "6BE826CB-6B30-4AEC-8777-2E90464633DE"
|
||||||
|
process.env.NODE_ENV = "cypress"
|
||||||
|
|
||||||
|
initialiseBudibase({ dir: homedir, clientId: "cypress-test" })
|
||||||
|
.then(() => {
|
||||||
delete require.cache[require.resolve("../../server/src/environment")]
|
delete require.cache[require.resolve("../../server/src/environment")]
|
||||||
run({ dir: homedir })
|
run({ dir: homedir })
|
||||||
})
|
})
|
||||||
|
.catch(e => console.error(e))
|
||||||
|
|
|
@ -24,69 +24,89 @@
|
||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
|
||||||
Cypress.Commands.add("createApp", (name, description) => {
|
Cypress.Commands.add("createApp", name => {
|
||||||
cy.get(".banner-button")
|
cy.contains("Create New Web App").click()
|
||||||
.click()
|
|
||||||
.get('input[name="name"]')
|
cy.get("body")
|
||||||
|
.then($body => {
|
||||||
|
if ($body.find("input[name=apiKey]").length) {
|
||||||
|
// input was found, do something else here
|
||||||
|
cy.get("input[name=apiKey]")
|
||||||
|
.type(name)
|
||||||
|
.should("have.value", name)
|
||||||
|
cy.contains("Next").click()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
cy.get("input[name=applicationName]")
|
||||||
.type(name)
|
.type(name)
|
||||||
.should("have.value", name)
|
.should("have.value", name)
|
||||||
|
|
||||||
cy.get('textarea[name="description"]')
|
cy.contains("Next").click()
|
||||||
.type(description)
|
|
||||||
.should("have.value", description)
|
|
||||||
|
|
||||||
cy.contains("Save").click()
|
cy.get("input[name=username]")
|
||||||
})
|
|
||||||
Cypress.Commands.add("createModel", (modelName, firstField, secondField) => {
|
|
||||||
// Enter model name
|
|
||||||
cy.get("[data-cy=Name]")
|
|
||||||
.click()
|
.click()
|
||||||
.type(modelName)
|
.type("test")
|
||||||
|
cy.get("input[name=password]")
|
||||||
|
.click()
|
||||||
|
.type("test")
|
||||||
|
cy.contains("Submit").click()
|
||||||
|
cy.contains("Create New Table", {
|
||||||
|
timeout: 10000,
|
||||||
|
}).should("be.visible")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Cypress.Commands.add("createTable", tableName => {
|
||||||
|
// Enter model name
|
||||||
|
cy.contains("Create New Table").click()
|
||||||
|
cy.get("[placeholder='Table Name']").type(tableName)
|
||||||
|
|
||||||
// Add 'name' field
|
// Add 'name' field
|
||||||
cy.get("[data-cy=add-new-model-field]").click()
|
|
||||||
cy.get("[data-cy=Name]")
|
|
||||||
.click()
|
|
||||||
.type(firstField)
|
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
|
cy.contains(tableName).should("be.visible")
|
||||||
|
})
|
||||||
|
|
||||||
// Add 'age' field
|
Cypress.Commands.add("addColumn", (tableName, columnName, type) => {
|
||||||
cy.get("[data-cy=add-new-model-field]").click()
|
// Select Table
|
||||||
|
cy.contains(tableName).click()
|
||||||
|
cy.contains("Create New Column").click()
|
||||||
|
|
||||||
cy.get("[data-cy=Name]")
|
cy.get("[placeholder=Name]").type(columnName)
|
||||||
.click()
|
cy.get("select").select(type)
|
||||||
.type(secondField)
|
|
||||||
cy.get("select").select("number")
|
cy.contains("Save Column")
|
||||||
cy.contains("Save").click()
|
|
||||||
cy.contains(secondField).should("exist")
|
|
||||||
|
|
||||||
// Save model
|
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
})
|
})
|
||||||
Cypress.Commands.add("addRecord", (firstField, secondField) => {
|
|
||||||
cy.contains("Create new record").click()
|
|
||||||
|
|
||||||
cy.get("[data-cy=name-input]")
|
Cypress.Commands.add("addRecord", values => {
|
||||||
.click()
|
cy.contains("Create New Row").click()
|
||||||
.type(firstField)
|
|
||||||
cy.get("[data-cy=age-input]")
|
for (let i = 0; i < values.length; i++) {
|
||||||
.click()
|
cy.get("input")
|
||||||
.type(secondField)
|
.eq(i)
|
||||||
|
.type(values[i])
|
||||||
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.contains("Save").click()
|
cy.contains("Save").click()
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createUser", (username, password, level) => {
|
Cypress.Commands.add("createUser", (username, password) => {
|
||||||
// Create User
|
// Create User
|
||||||
cy.get(".nav-group-header > .ri-add-line").click()
|
cy.get(".toprightnav > .settings").click()
|
||||||
|
cy.contains("Users").click()
|
||||||
|
|
||||||
cy.get("[data-cy=username]").type(username)
|
cy.get("[name=Name]")
|
||||||
cy.get("[data-cy=password]").type(password)
|
.first()
|
||||||
cy.get("[data-cy=accessLevel]").select(level)
|
.type(username)
|
||||||
|
cy.get("[name=Password]")
|
||||||
|
.first()
|
||||||
|
.type(password)
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
cy.contains("Save").click()
|
cy.get(".create-button").click()
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("addHeadlineComponent", text => {
|
Cypress.Commands.add("addHeadlineComponent", text => {
|
||||||
|
@ -95,7 +115,8 @@ Cypress.Commands.add("addHeadlineComponent", text => {
|
||||||
cy.get("[data-cy=Text]").click()
|
cy.get("[data-cy=Text]").click()
|
||||||
cy.get("[data-cy=Headline]").click()
|
cy.get("[data-cy=Headline]").click()
|
||||||
cy.get(".tabs > :nth-child(2)").click()
|
cy.get(".tabs > :nth-child(2)").click()
|
||||||
cy.get('input[type="text"]').type(text)
|
cy.contains("Settings").click()
|
||||||
|
cy.get('input[name="text"]').type(text)
|
||||||
cy.contains("Design").click()
|
cy.contains("Design").click()
|
||||||
})
|
})
|
||||||
Cypress.Commands.add("addButtonComponent", () => {
|
Cypress.Commands.add("addButtonComponent", () => {
|
||||||
|
@ -105,9 +126,7 @@ Cypress.Commands.add("addButtonComponent", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("navigateToFrontend", () => {
|
Cypress.Commands.add("navigateToFrontend", () => {
|
||||||
cy.get(".close", { timeout: 10000 }).click()
|
|
||||||
cy.contains("frontend").click()
|
cy.contains("frontend").click()
|
||||||
cy.get(".close", { timeout: 10000 }).click()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Cypress.Commands.add("createScreen", (screenName, route) => {
|
Cypress.Commands.add("createScreen", (screenName, route) => {
|
||||||
|
|
|
@ -19,3 +19,4 @@ import "./commands"
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
// Alternatively you can use CommonJS syntax:
|
||||||
// require('./commands')
|
// require('./commands')
|
||||||
|
require("cypress-terminal-report/src/installLogsCollector")()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -13,7 +13,9 @@
|
||||||
"cy:setup": "node ./cypress/setup.js",
|
"cy:setup": "node ./cypress/setup.js",
|
||||||
"cy:run": "cypress run",
|
"cy:run": "cypress run",
|
||||||
"cy:open": "cypress open",
|
"cy:open": "cypress open",
|
||||||
"cy:test": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run"
|
"cy:run:ci": "cypress run --browser electron --record --key f308590b-6070-41af-b970-794a3823d451",
|
||||||
|
"cy:test": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run",
|
||||||
|
"cy:ci": "start-server-and-test cy:setup http://localhost:4001/_builder cy:run:ci"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"globals": {
|
"globals": {
|
||||||
|
@ -55,23 +57,24 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.18.0",
|
"@budibase/bbui": "^1.23.1",
|
||||||
"@budibase/client": "^0.1.1",
|
"@budibase/client": "^0.1.17",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
"@nx-js/compiler-util": "^2.0.0",
|
||||||
"britecharts": "^2.16.0",
|
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@svelteschool/svelte-forms": "^0.7.0",
|
"@svelteschool/svelte-forms": "^0.7.0",
|
||||||
|
"britecharts": "^2.16.0",
|
||||||
"codemirror": "^5.51.0",
|
"codemirror": "^5.51.0",
|
||||||
"d3-selection": "^1.4.1",
|
"d3-selection": "^1.4.1",
|
||||||
"date-fns": "^1.29.0",
|
"date-fns": "^1.29.0",
|
||||||
"deepmerge": "^4.2.2",
|
"deepmerge": "^4.2.2",
|
||||||
|
"fast-sort": "^2.2.0",
|
||||||
"feather-icons": "^4.21.0",
|
"feather-icons": "^4.21.0",
|
||||||
"flatpickr": "^4.5.7",
|
"flatpickr": "^4.5.7",
|
||||||
"lodash": "^4.17.13",
|
"lodash": "^4.17.13",
|
||||||
"lunr": "^2.3.5",
|
"lunr": "^2.3.5",
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.0.1",
|
||||||
"posthog-js": "^1.3.1",
|
"posthog-js": "1.3.1",
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"shortid": "^2.2.15",
|
"shortid": "^2.2.15",
|
||||||
"string_decoder": "^1.2.0",
|
"string_decoder": "^1.2.0",
|
||||||
|
@ -93,8 +96,10 @@
|
||||||
"babel-jest": "^24.8.0",
|
"babel-jest": "^24.8.0",
|
||||||
"browser-sync": "^2.26.7",
|
"browser-sync": "^2.26.7",
|
||||||
"cypress": "^4.8.0",
|
"cypress": "^4.8.0",
|
||||||
|
"cypress-terminal-report": "^1.4.1",
|
||||||
"eslint-plugin-cypress": "^2.11.1",
|
"eslint-plugin-cypress": "^2.11.1",
|
||||||
"http-proxy-middleware": "^0.19.1",
|
"http-proxy-middleware": "^0.19.1",
|
||||||
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "^24.8.0",
|
"jest": "^24.8.0",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import posthog from "posthog-js"
|
||||||
|
|
||||||
function activate() {
|
function activate() {
|
||||||
Sentry.init({ dsn: process.env.SENTRY_DSN })
|
Sentry.init({ dsn: process.env.SENTRY_DSN })
|
||||||
|
if (!process.env.POSTHOG_TOKEN) return
|
||||||
posthog.init(process.env.POSTHOG_TOKEN, {
|
posthog.init(process.env.POSTHOG_TOKEN, {
|
||||||
api_host: process.env.POSTHOG_URL,
|
api_host: process.env.POSTHOG_URL,
|
||||||
})
|
})
|
||||||
|
@ -13,7 +14,7 @@ function captureException(err) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function captureEvent(event) {
|
function captureEvent(event) {
|
||||||
if (process.env.NODE_ENV !== "production") return
|
if (!process.env.POSTHOG_TOKEN) return
|
||||||
posthog.capture(event)
|
posthog.capture(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,10 +61,9 @@ export const getBackendUiStore = () => {
|
||||||
state.draftModel = cloneDeep(model)
|
state.draftModel = cloneDeep(model)
|
||||||
state.selectedField = ""
|
state.selectedField = ""
|
||||||
state.selectedView = `all_${model._id}`
|
state.selectedView = `all_${model._id}`
|
||||||
state.tabs.SETUP_PANEL = "SETUP"
|
|
||||||
return state
|
return state
|
||||||
}),
|
}),
|
||||||
save: async ({ model }) => {
|
save: async model => {
|
||||||
const updatedModel = cloneDeep(model)
|
const updatedModel = cloneDeep(model)
|
||||||
|
|
||||||
// update any renamed schema keys to reflect their names
|
// update any renamed schema keys to reflect their names
|
||||||
|
@ -83,20 +82,35 @@ export const getBackendUiStore = () => {
|
||||||
const savedModel = await response.json()
|
const savedModel = await response.json()
|
||||||
await store.actions.models.fetch()
|
await store.actions.models.fetch()
|
||||||
store.actions.models.select(savedModel)
|
store.actions.models.select(savedModel)
|
||||||
|
return savedModel
|
||||||
},
|
},
|
||||||
addField: field => {
|
delete: async model => {
|
||||||
|
await api.delete(`/api/models/${model._id}/${model._rev}`)
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
if (!state.draftModel.schema) {
|
state.models = state.models.filter(
|
||||||
state.draftModel.schema = {}
|
existing => existing._id !== model._id
|
||||||
}
|
)
|
||||||
|
state.selectedModel = state.models[0] || {}
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
|
saveField: ({ originalName, field }) => {
|
||||||
|
store.update(state => {
|
||||||
|
// delete the original if renaming
|
||||||
|
delete state.draftModel.schema[originalName]
|
||||||
|
|
||||||
state.draftModel.schema = {
|
state.draftModel.schema = {
|
||||||
...state.draftModel.schema,
|
...state.draftModel.schema,
|
||||||
[field.name]: cloneDeep(field),
|
[field.name]: cloneDeep(field),
|
||||||
}
|
}
|
||||||
state.selectedField = field.name
|
store.actions.models.save(state.draftModel)
|
||||||
state.tabs.NAVIGATION_PANEL = "NAVIGATE"
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteField: field => {
|
||||||
|
store.update(state => {
|
||||||
|
delete state.draftModel.schema[field.name]
|
||||||
|
store.actions.models.save(state.draftModel)
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,38 +1,19 @@
|
||||||
<script>
|
<script>
|
||||||
import { onMount, getContext } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
import fsort from "fast-sort"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import { Button } from "@budibase/bbui"
|
import { Button, Icon } from "@budibase/bbui"
|
||||||
import Select from "components/common/Select.svelte"
|
import Select from "components/common/Select.svelte"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
import LinkedRecord from "./LinkedRecord.svelte"
|
import LinkedRecord from "./LinkedRecord.svelte"
|
||||||
import TablePagination from "./TablePagination.svelte"
|
import TablePagination from "./TablePagination.svelte"
|
||||||
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
|
import { DeleteRecordModal, CreateEditRecordModal } from "./modals"
|
||||||
|
import RowPopover from "./popovers/Row.svelte"
|
||||||
|
import ColumnPopover from "./popovers/Column.svelte"
|
||||||
|
import ColumnHeaderPopover from "./popovers/ColumnHeader.svelte"
|
||||||
|
import EditRowPopover from "./popovers/EditRow.svelte"
|
||||||
import * as api from "./api"
|
import * as api from "./api"
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
|
||||||
|
|
||||||
const editRecord = async row => {
|
|
||||||
open(
|
|
||||||
CreateEditRecordModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
record: row,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteRecord = async row => {
|
|
||||||
open(
|
|
||||||
DeleteRecordModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
record: row,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const ITEMS_PER_PAGE = 10
|
const ITEMS_PER_PAGE = 10
|
||||||
// Internal headers we want to hide from the user
|
// Internal headers we want to hide from the user
|
||||||
const INTERNAL_HEADERS = ["_id", "_rev", "modelId", "type"]
|
const INTERNAL_HEADERS = ["_id", "_rev", "modelId", "type"]
|
||||||
|
@ -58,6 +39,8 @@
|
||||||
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
currentPage * ITEMS_PER_PAGE + ITEMS_PER_PAGE
|
||||||
)
|
)
|
||||||
: []
|
: []
|
||||||
|
$: sort = $backendUiStore.sort
|
||||||
|
$: sorted = sort ? fsort(data)[sort.direction](sort.column) : data
|
||||||
|
|
||||||
$: headers = Object.keys($backendUiStore.selectedModel.schema).filter(
|
$: headers = Object.keys($backendUiStore.selectedModel.schema).filter(
|
||||||
id => !INTERNAL_HEADERS.includes(id)
|
id => !INTERNAL_HEADERS.includes(id)
|
||||||
|
@ -85,53 +68,41 @@
|
||||||
<section>
|
<section>
|
||||||
<div class="table-controls">
|
<div class="table-controls">
|
||||||
<h2 class="title">{$backendUiStore.selectedModel.name}</h2>
|
<h2 class="title">{$backendUiStore.selectedModel.name}</h2>
|
||||||
<Button primary on:click={createNewRecord}>
|
<div class="popovers">
|
||||||
<span class="button-inner">Create New Record</span>
|
<ColumnPopover />
|
||||||
</Button>
|
{#if Object.keys($backendUiStore.selectedModel.schema).length > 0}
|
||||||
|
<RowPopover />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table class="uk-table">
|
<table class="uk-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Edit</th>
|
<th class="edit-header">
|
||||||
|
<div>Edit</div>
|
||||||
|
</th>
|
||||||
{#each headers as header}
|
{#each headers as header}
|
||||||
<th>{$backendUiStore.selectedModel.schema[header].name}</th>
|
<th>
|
||||||
|
<ColumnHeaderPopover
|
||||||
|
field={$backendUiStore.selectedModel.schema[header]} />
|
||||||
|
</th>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#if paginatedData.length === 0}
|
{#if sorted.length === 0}
|
||||||
<div class="no-data">No Data.</div>
|
<div class="no-data">No Data.</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#each paginatedData as row}
|
{#each sorted as row}
|
||||||
<tr class="hoverable">
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<div class="uk-inline">
|
<EditRowPopover {row} />
|
||||||
<i class="ri-more-line" />
|
|
||||||
<div uk-dropdown="mode: click">
|
|
||||||
<ul class="uk-nav uk-dropdown-nav">
|
|
||||||
<li
|
|
||||||
on:click={() => {
|
|
||||||
editRecord(row)
|
|
||||||
}}>
|
|
||||||
<i class="ri-edit-line" />
|
|
||||||
<div class="label">Edit</div>
|
|
||||||
</li>
|
|
||||||
<li
|
|
||||||
on:click={() => {
|
|
||||||
deleteRecord(row)
|
|
||||||
}}>
|
|
||||||
<i class="ri-delete-bin-2-line" />
|
|
||||||
<div class="label">Delete</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
</td>
|
||||||
{#each headers as header}
|
{#each headers as header}
|
||||||
<td>
|
<td>
|
||||||
{#if schema[header].type === 'link'}
|
{#if schema[header].type === 'link'}
|
||||||
<LinkedRecord field={schema[header]} ids={row[header]} />
|
<LinkedRecord field={schema[header]} ids={row[header]} />
|
||||||
{:else}{row[header]}{/if}
|
{:else}{row[header] || ''}{/if}
|
||||||
</td>
|
</td>
|
||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -164,7 +135,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
background: var(--blue-light);
|
height: 40px;
|
||||||
|
background: var(--grey-3);
|
||||||
border: 1px solid var(--grey-4);
|
border: 1px solid var(--grey-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +146,28 @@
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
transition: 0.5s all;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-header {
|
||||||
|
width: 100px;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit-header:hover {
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
th:hover {
|
||||||
|
color: var(--blue);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
max-width: 200px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
border: 1px solid var(--grey-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody tr {
|
tbody tr {
|
||||||
|
@ -188,47 +182,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-controls {
|
.table-controls {
|
||||||
display: flex;
|
width: 100%;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: baseline;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ri-more-line:hover,
|
.popovers {
|
||||||
.uk-dropdown-nav li:hover {
|
display: flex;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-data {
|
.no-data {
|
||||||
padding: 20px;
|
padding: 14px;
|
||||||
}
|
|
||||||
|
|
||||||
.button-inner {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
color: var(--grey-7);
|
|
||||||
margin-right: 8px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
color: var(--grey-7);
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: inter;
|
|
||||||
font-weight: 400;
|
|
||||||
margin: 12px 0px;
|
|
||||||
}
|
|
||||||
.label:hover {
|
|
||||||
color: var(--ink);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
<script>
|
|
||||||
import { store } from "builderStore"
|
|
||||||
import Modal from "components/common/Modal.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import * as api from "../api"
|
|
||||||
|
|
||||||
export let onClosed
|
|
||||||
|
|
||||||
let databaseName
|
|
||||||
|
|
||||||
async function createDatabase() {
|
|
||||||
const response = await api.createDatabase($store.appId, databaseName)
|
|
||||||
store.createDatabaseForApp(response)
|
|
||||||
onClosed()
|
|
||||||
}
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
section {
|
|
||||||
padding: 30px;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--grey-1);
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
import { Input, TextArea, Button, Select } from "@budibase/bbui"
|
||||||
|
import { store, backendUiStore } from "builderStore"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import Dropdown from "components/common/Dropdown.svelte"
|
||||||
|
import Textbox from "components/common/Textbox.svelte"
|
||||||
|
import ButtonGroup from "components/common/ButtonGroup.svelte"
|
||||||
|
import NumberBox from "components/common/NumberBox.svelte"
|
||||||
|
import ValuesList from "components/common/ValuesList.svelte"
|
||||||
|
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
||||||
|
import Checkbox from "components/common/Checkbox.svelte"
|
||||||
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
import DatePicker from "components/common/DatePicker.svelte"
|
||||||
|
import LinkedRecordSelector from "components/common/LinkedRecordSelector.svelte"
|
||||||
|
import * as api from "../api"
|
||||||
|
|
||||||
|
export let onClosed
|
||||||
|
export let field = {}
|
||||||
|
|
||||||
|
let originalName = field.name
|
||||||
|
|
||||||
|
$: required =
|
||||||
|
field.constraints &&
|
||||||
|
field.constraints.presence &&
|
||||||
|
!field.constraints.presence.allowEmpty
|
||||||
|
$: if (field.type) {
|
||||||
|
field.constraints = FIELDS[field.type.toUpperCase()].constraints
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveColumn() {
|
||||||
|
backendUiStore.update(state => {
|
||||||
|
backendUiStore.actions.models.saveField({
|
||||||
|
originalName,
|
||||||
|
field,
|
||||||
|
})
|
||||||
|
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
onClosed()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<Input placeholder="Name" thin bind:value={field.name} />
|
||||||
|
|
||||||
|
<Select secondary thin bind:value={field.type}>
|
||||||
|
{#each Object.values(FIELDS) as field}
|
||||||
|
<option value={field.type}>{field.name}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="field">
|
||||||
|
<label>Required</label>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
bind:checked={required}
|
||||||
|
on:change={() => (field.constraints.presence.allowEmpty = required)} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if field.type === 'string'}
|
||||||
|
<NumberBox
|
||||||
|
label="Max Length"
|
||||||
|
bind:value={field.constraints.length.maximum} />
|
||||||
|
<ValuesList
|
||||||
|
label="Categories"
|
||||||
|
bind:values={field.constraints.inclusion} />
|
||||||
|
{:else if field.type === 'datetime'}
|
||||||
|
<DatePicker
|
||||||
|
label="Min Value"
|
||||||
|
bind:value={field.constraints.datetime.earliest} />
|
||||||
|
<DatePicker
|
||||||
|
label="Max Value"
|
||||||
|
bind:value={field.constraints.datetime.latest} />
|
||||||
|
{:else if field.type === 'number'}
|
||||||
|
<NumberBox
|
||||||
|
label="Min Value"
|
||||||
|
bind:value={field.constraints.numericality.greaterThanOrEqualTo} />
|
||||||
|
<NumberBox
|
||||||
|
label="Max Value"
|
||||||
|
bind:value={field.constraints.numericality.lessThanOrEqualTo} />
|
||||||
|
{:else if field.type === 'link'}
|
||||||
|
<div class="field">
|
||||||
|
<label>Link</label>
|
||||||
|
<select class="budibase__input" bind:value={field.modelId}>
|
||||||
|
<option value={''} />
|
||||||
|
{#each $backendUiStore.models as model}
|
||||||
|
{#if model._id !== $backendUiStore.draftModel._id}
|
||||||
|
<option value={model._id}>{model.name}</option>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="button-margin-3">
|
||||||
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
<div class="button-margin-4">
|
||||||
|
<Button primary on:click={saveColumn}>Save Column</Button>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.actions {
|
||||||
|
padding: var(--spacing-l) var(--spacing-xl);
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px 30px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
background: var(--grey-1);
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 20px 1fr;
|
||||||
|
align-items: center;
|
||||||
|
grid-gap: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: var(--spacing-l);
|
||||||
|
font-family: var(--font-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-3 {
|
||||||
|
grid-column-start: 3;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-4 {
|
||||||
|
grid-column-start: 4;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,165 +0,0 @@
|
||||||
<script>
|
|
||||||
import { tick } from "svelte"
|
|
||||||
import Textbox from "components/common/Textbox.svelte"
|
|
||||||
import Button from "components/common/Button.svelte"
|
|
||||||
import Select from "components/common/Select.svelte"
|
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
|
||||||
import getIcon from "components/common/icon"
|
|
||||||
import FieldView from "./FieldView.svelte"
|
|
||||||
import api from "builderStore/api"
|
|
||||||
import { store, backendUiStore } from "builderStore"
|
|
||||||
import { pipe } from "components/common/core"
|
|
||||||
import ErrorsBox from "components/common/ErrorsBox.svelte"
|
|
||||||
|
|
||||||
export let model = { schema: {} }
|
|
||||||
export let onClosed
|
|
||||||
|
|
||||||
let showFieldView = false
|
|
||||||
let fieldToEdit
|
|
||||||
|
|
||||||
$: modelFields = model.schema ? Object.entries(model.schema) : []
|
|
||||||
|
|
||||||
function editField() {}
|
|
||||||
|
|
||||||
function deleteField() {}
|
|
||||||
|
|
||||||
function onFinishedFieldEdit() {}
|
|
||||||
|
|
||||||
async function saveModel() {
|
|
||||||
const SAVE_MODEL_URL = `/api/models`
|
|
||||||
const response = await api.post(SAVE_MODEL_URL, model)
|
|
||||||
const newModel = await response.json()
|
|
||||||
backendUiStore.actions.models.create(newModel)
|
|
||||||
onClosed()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="heading">
|
|
||||||
{#if !showFieldView}
|
|
||||||
<i class="ri-list-settings-line button--toggled" />
|
|
||||||
<h3 class="budibase__title--3">Create / Edit Table</h3>
|
|
||||||
{:else}
|
|
||||||
<i class="ri-file-list-line button--toggled" />
|
|
||||||
<h3 class="budibase__title--3">Create / Edit Field</h3>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if !showFieldView}
|
|
||||||
<div class="padding">
|
|
||||||
{#if $store.errors && $store.errors.length > 0}
|
|
||||||
<ErrorsBox errors={$store.errors} />
|
|
||||||
{/if}
|
|
||||||
<div class="textbox">
|
|
||||||
<Textbox label="Name" bind:text={model.name} />
|
|
||||||
</div>
|
|
||||||
<div class="table-controls">
|
|
||||||
<span class="label">Fields</span>
|
|
||||||
<div
|
|
||||||
data-cy="add-new-model-field"
|
|
||||||
class="hoverable new-field"
|
|
||||||
on:click={() => (showFieldView = true)}>
|
|
||||||
Add new field
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="uk-table fields-table budibase__table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Edit</th>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th />
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#each modelFields as [key, meta]}
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<i class="ri-more-line" on:click={() => editField(meta)} />
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div>{key}</div>
|
|
||||||
</td>
|
|
||||||
<td>{meta.type}</td>
|
|
||||||
<td>
|
|
||||||
<i
|
|
||||||
class="ri-delete-bin-6-line hoverable"
|
|
||||||
on:click={() => deleteField(meta)} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<footer>
|
|
||||||
<ActionButton color="secondary" on:click={saveModel}>Save</ActionButton>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<FieldView
|
|
||||||
field={fieldToEdit}
|
|
||||||
onFinished={onFinishedFieldEdit}
|
|
||||||
schema={model.schema}
|
|
||||||
goBack={() => (showFieldView = false)} />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.padding {
|
|
||||||
padding-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textbox {
|
|
||||||
margin: 0px 40px 0px 40px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-field {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fields-table {
|
|
||||||
margin: 8px 40px 0px 40px;
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 88%;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody > tr:hover {
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-controls {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ri-more-line:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.heading {
|
|
||||||
padding: 40px 40px 0 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
margin: 0 0 0 10px;
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
margin-top: 40px;
|
|
||||||
padding: 20px 40px 20px 40px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -20,10 +20,6 @@
|
||||||
? Object.entries($backendUiStore.selectedModel.schema)
|
? Object.entries($backendUiStore.selectedModel.schema)
|
||||||
: []
|
: []
|
||||||
|
|
||||||
function closed() {
|
|
||||||
onClosed()
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelect = meta =>
|
const isSelect = meta =>
|
||||||
meta.type === "string" &&
|
meta.type === "string" &&
|
||||||
meta.constraints &&
|
meta.constraints &&
|
||||||
|
@ -68,10 +64,6 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<header>
|
|
||||||
<i class="ri-file-user-fill" />
|
|
||||||
<h4>Create / Edit Record</h4>
|
|
||||||
</header>
|
|
||||||
<ErrorsBox {errors} />
|
<ErrorsBox {errors} />
|
||||||
<form on:submit|preventDefault class="uk-form-stacked">
|
<form on:submit|preventDefault class="uk-form-stacked">
|
||||||
{#each modelSchema as [key, meta]}
|
{#each modelSchema as [key, meta]}
|
||||||
|
@ -97,42 +89,13 @@
|
||||||
<Button secondary on:click={onClosed}>Cancel</Button>
|
<Button secondary on:click={onClosed}>Cancel</Button>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-margin-4">
|
<div class="button-margin-4">
|
||||||
<Button blue on:click={saveRecord}>Save</Button>
|
<Button primary on:click={saveRecord}>Save</Button>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
header {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
display: grid;
|
|
||||||
grid-gap: 20px;
|
|
||||||
grid-template-columns: 40px 1fr;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: var(--blue-light);
|
|
||||||
color: var(--grey-7);
|
|
||||||
font-size: 20px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 24px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-family: sans-serif;
|
|
||||||
color: var(--ink);
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
padding: 30px;
|
padding: var(--spacing-l) var(--spacing-xl);
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
<script>
|
||||||
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { store, backendUiStore } from "builderStore"
|
||||||
|
import * as api from "../api"
|
||||||
|
|
||||||
|
export let table
|
||||||
|
export let onClosed
|
||||||
|
|
||||||
|
function deleteTable() {
|
||||||
|
backendUiStore.actions.models.delete(table)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="content">
|
||||||
|
<heading>
|
||||||
|
<i class="ri-information-line alert" />
|
||||||
|
<h4 class="budibase__title--4">Delete Table</h4>
|
||||||
|
</heading>
|
||||||
|
<p>
|
||||||
|
Are you sure you want to delete this table? 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
|
||||||
|
alert
|
||||||
|
on:click={async () => {
|
||||||
|
await backendUiStore.actions.models.delete(table)
|
||||||
|
notifier.danger('Table deleted')
|
||||||
|
onClosed()
|
||||||
|
}}>
|
||||||
|
Delete
|
||||||
|
</ActionButton>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.alert {
|
||||||
|
color: rgba(255, 0, 31, 1);
|
||||||
|
background: var(--grey-1);
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--grey-1);
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
heading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,18 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
|
import { Input, Select } from "@budibase/bbui"
|
||||||
|
|
||||||
export let type = "text"
|
export let type = "text"
|
||||||
export let value = ""
|
export let value = ""
|
||||||
export let label
|
export let label
|
||||||
export let errors = []
|
|
||||||
export let options = []
|
export let options = []
|
||||||
|
|
||||||
let checked = type === "checkbox" ? value : false
|
let checked = type === "checkbox" ? value : false
|
||||||
|
|
||||||
const determineClassName = type => {
|
|
||||||
if (type === "checkbox") return "uk-checkbox"
|
|
||||||
if (type === "select") return "uk-select"
|
|
||||||
return "uk-input"
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleInput = event => {
|
const handleInput = event => {
|
||||||
if (event.target.type === "checkbox") {
|
if (event.target.type === "checkbox") {
|
||||||
value = event.target.checked
|
value = event.target.checked
|
||||||
|
@ -28,24 +23,21 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label>{label}</label>
|
|
||||||
|
|
||||||
{#if type === 'select'}
|
{#if type === 'select'}
|
||||||
<select
|
<Select thin secondary data-cy="{label}-select" bind:value>
|
||||||
data-cy="{label}-select"
|
|
||||||
class={determineClassName(type)}
|
|
||||||
bind:value
|
|
||||||
class:uk-form-danger={errors.length > 0}>
|
|
||||||
<option />
|
<option />
|
||||||
{#each options as opt}
|
{#each options as opt}
|
||||||
<option value={opt}>{opt}</option>
|
<option value={opt}>{opt}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</Select>
|
||||||
{:else}
|
{:else}
|
||||||
<input
|
{#if type === 'checkbox'}
|
||||||
|
<label>{label}</label>
|
||||||
|
{/if}
|
||||||
|
<Input
|
||||||
|
thin
|
||||||
|
placeholder={label}
|
||||||
data-cy="{label}-input"
|
data-cy="{label}-input"
|
||||||
class={determineClassName(type)}
|
|
||||||
class:uk-form-danger={errors.length > 0}
|
|
||||||
{checked}
|
{checked}
|
||||||
{type}
|
{type}
|
||||||
{value}
|
{value}
|
||||||
|
@ -55,13 +47,9 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
label {
|
label {
|
||||||
display: block;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-bottom: 12px;
|
font-size: var(--font-size-s);
|
||||||
}
|
float: left;
|
||||||
|
margin-right: 8px;
|
||||||
input {
|
|
||||||
color: var(--dark-grey);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
export { default as DeleteRecordModal } from "./DeleteRecord.svelte"
|
export { default as DeleteRecordModal } from "./DeleteRecord.svelte"
|
||||||
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte"
|
export { default as CreateEditRecordModal } from "./CreateEditRecord.svelte"
|
||||||
export { default as CreateEditViewModal } from "./CreateEditView.svelte"
|
export { default as CreateEditViewModal } from "./CreateEditView.svelte"
|
||||||
export { default as CreateDatabaseModal } from "./CreateDatabase.svelte"
|
|
||||||
export { default as CreateUserModal } from "./CreateUser.svelte"
|
export { default as CreateUserModal } from "./CreateUser.svelte"
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import { ModelSetupNav } from "components/nav/ModelSetupNav"
|
||||||
|
import ModelFieldEditor from "components/nav/ModelSetupNav/ModelFieldEditor.svelte"
|
||||||
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
let fieldName
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<Button text small on:click={dropdown.show}>
|
||||||
|
<Icon name="addcolumn" />
|
||||||
|
Create New Column
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
<h5>Create Column</h5>
|
||||||
|
<CreateEditColumn onClosed={dropdown.hide} />
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,107 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import { ModelSetupNav } from "components/nav/ModelSetupNav"
|
||||||
|
import ModelFieldEditor from "components/nav/ModelSetupNav/ModelFieldEditor.svelte"
|
||||||
|
import CreateEditColumn from "../modals/CreateEditColumn.svelte"
|
||||||
|
|
||||||
|
export let field
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
|
||||||
|
let editing
|
||||||
|
|
||||||
|
$: sortColumn = $backendUiStore.sort && $backendUiStore.sort.column
|
||||||
|
$: sortDirection = $backendUiStore.sort && $backendUiStore.sort.direction
|
||||||
|
|
||||||
|
function showEditor() {
|
||||||
|
editing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideEditor() {
|
||||||
|
dropdown.hide()
|
||||||
|
editing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteField() {
|
||||||
|
backendUiStore.actions.models.deleteField(field)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sort(direction, column) {
|
||||||
|
backendUiStore.update(state => {
|
||||||
|
state.sort = { direction, column }
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
hideEditor()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor} on:click={dropdown.show}>
|
||||||
|
{field.name}
|
||||||
|
<Icon name="arrowdown" />
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
{#if editing}
|
||||||
|
<h5>Edit Column</h5>
|
||||||
|
<CreateEditColumn onClosed={hideEditor} {field} />
|
||||||
|
{:else}
|
||||||
|
<ul>
|
||||||
|
<li data-cy="edit-column-header" on:click={showEditor}>
|
||||||
|
<Icon name="edit" />
|
||||||
|
Edit
|
||||||
|
</li>
|
||||||
|
<li data-cy="delete-column-header" on:click={deleteField}>
|
||||||
|
<Icon name="delete" />
|
||||||
|
Delete
|
||||||
|
</li>
|
||||||
|
{#if sortDirection === 'desc' || sortColumn !== field.name}
|
||||||
|
<li on:click={() => sort('asc', field.name)}>
|
||||||
|
<Icon name="sortascending" />
|
||||||
|
Sort A - Z
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
{#if sortDirection === 'asc' || sortColumn !== field.name}
|
||||||
|
<li on:click={() => sort('desc', field.name)}>
|
||||||
|
<Icon name="sortdescending" />
|
||||||
|
Sort Z - A
|
||||||
|
</li>
|
||||||
|
{/if}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--spacing-s) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: var(--spacing-s) var(--spacing-m);
|
||||||
|
margin: auto 0px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li:active {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,97 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import CreateEditRecord from "../modals/CreateEditRecord.svelte"
|
||||||
|
import DeleteRecordModal from "../modals/DeleteRecord.svelte"
|
||||||
|
|
||||||
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
|
export let row
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
|
||||||
|
let editing
|
||||||
|
|
||||||
|
function showEditor() {
|
||||||
|
editing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideEditor() {
|
||||||
|
dropdown.hide()
|
||||||
|
editing = false
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteRow = () => {
|
||||||
|
open(
|
||||||
|
DeleteRecordModal,
|
||||||
|
{
|
||||||
|
onClosed: hideEditor,
|
||||||
|
record: row,
|
||||||
|
},
|
||||||
|
{ styleContent: { padding: "0" } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor} on:click={dropdown.show}>
|
||||||
|
<i class="ri-more-line" />
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
{#if editing}
|
||||||
|
<h5>Edit Row</h5>
|
||||||
|
<CreateEditRecord onClosed={hideEditor} record={row} />
|
||||||
|
{:else}
|
||||||
|
<ul>
|
||||||
|
<li data-cy="edit-row" on:click={showEditor}>
|
||||||
|
<Icon name="edit" />
|
||||||
|
Edit
|
||||||
|
</li>
|
||||||
|
<li data-cy="delete-row" on:click={deleteRow}>
|
||||||
|
<Icon name="delete" />
|
||||||
|
Delete
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ri-more-line:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--spacing-s) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: var(--spacing-s) var(--spacing-m);
|
||||||
|
margin: auto 0px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li:active {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import { DropdownMenu, Button, Icon } from "@budibase/bbui"
|
||||||
|
import CreateEditRecord from "../modals/CreateEditRecord.svelte"
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<Button text small on:click={dropdown.show}>
|
||||||
|
<Icon name="addrow" />
|
||||||
|
Create New Row
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
<h5>Add New Row</h5>
|
||||||
|
<CreateEditRecord onClosed={dropdown.hide} />
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,91 +0,0 @@
|
||||||
<script>
|
|
||||||
import * as blockDefinitions from "constants/backend"
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import Block from "components/common/Block.svelte"
|
|
||||||
|
|
||||||
const HEADINGS = [
|
|
||||||
{
|
|
||||||
title: "Fields",
|
|
||||||
key: "FIELDS",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Blocks",
|
|
||||||
key: "BLOCKS",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
let selectedTab = "FIELDS"
|
|
||||||
|
|
||||||
function addField(blockDefinition) {
|
|
||||||
backendUiStore.actions.models.addField(blockDefinition)
|
|
||||||
backendUiStore.actions.models.fetch()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section>
|
|
||||||
<header>
|
|
||||||
{#each HEADINGS as tab}
|
|
||||||
<span
|
|
||||||
class:selected={selectedTab === tab.key}
|
|
||||||
on:click={() => (selectedTab = tab.key)}>
|
|
||||||
{tab.title}
|
|
||||||
</span>
|
|
||||||
{/each}
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="block-grid">
|
|
||||||
{#each Object.values(blockDefinitions[selectedTab]) as blockDefinition}
|
|
||||||
<Block
|
|
||||||
on:click={() => addField(blockDefinition)}
|
|
||||||
title={blockDefinition.name}
|
|
||||||
icon={blockDefinition.icon} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
header {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(3, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
cursor: pointer;
|
|
||||||
display: grid;
|
|
||||||
justify-content: center;
|
|
||||||
align-content: center;
|
|
||||||
padding: 0px 16px;
|
|
||||||
height: 36px;
|
|
||||||
text-align: center;
|
|
||||||
background: #ffffff;
|
|
||||||
color: var(--grey-7);
|
|
||||||
border-radius: 5px;
|
|
||||||
font-family: inter;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
transition: all 0.3s;
|
|
||||||
text-rendering: optimizeLegibility;
|
|
||||||
border: none !important;
|
|
||||||
transition: 0.2s;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
span:hover {
|
|
||||||
color: var(--ink);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
background: var(--grey-3);
|
|
||||||
color: var(--ink);
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-grid {
|
|
||||||
margin-top: 20px;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
grid-gap: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
<script>
|
||||||
|
import { goto } from "@sveltech/routify"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
|
||||||
|
export let table
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
let name
|
||||||
|
|
||||||
|
async function saveTable() {
|
||||||
|
const model = await backendUiStore.actions.models.save({
|
||||||
|
name,
|
||||||
|
schema: {},
|
||||||
|
})
|
||||||
|
notifier.success(`Table ${name} created successfully.`)
|
||||||
|
$goto(`./model/${model._id}`)
|
||||||
|
dropdown.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor}>
|
||||||
|
<Button primary wide on:click={dropdown.show}>Create New Table</Button>
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
<div class="container">
|
||||||
|
<h5>Create Table</h5>
|
||||||
|
<Input
|
||||||
|
data-cy="table-name-input"
|
||||||
|
placeholder="Table Name"
|
||||||
|
thin
|
||||||
|
bind:value={name} />
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="button-margin-3">
|
||||||
|
<Button secondary on:click={dropdown.hide}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
<div class="button-margin-4">
|
||||||
|
<Button primary on:click={saveTable}>Save</Button>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
margin-bottom: var(--spacing-l);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: var(--spacing-l);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
background: var(--grey-1);
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-3 {
|
||||||
|
grid-column-start: 3;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-4 {
|
||||||
|
grid-column-start: 4;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,137 @@
|
||||||
|
<script>
|
||||||
|
import { getContext } from "svelte"
|
||||||
|
import { backendUiStore } from "builderStore"
|
||||||
|
import { DropdownMenu, Button, Icon, Input, Select } from "@budibase/bbui"
|
||||||
|
import { FIELDS } from "constants/backend"
|
||||||
|
import DeleteTableModal from "components/database/ModelDataTable/modals/DeleteTable.svelte"
|
||||||
|
|
||||||
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
|
export let table
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
|
||||||
|
let editing
|
||||||
|
|
||||||
|
function showEditor() {
|
||||||
|
editing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideEditor() {
|
||||||
|
dropdown.hide()
|
||||||
|
editing = false
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTable = () => {
|
||||||
|
open(
|
||||||
|
DeleteTableModal,
|
||||||
|
{
|
||||||
|
onClosed: close,
|
||||||
|
table,
|
||||||
|
},
|
||||||
|
{ styleContent: { padding: "0" } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function save() {
|
||||||
|
backendUiStore.actions.models.save(table)
|
||||||
|
hideEditor()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={anchor} on:click={dropdown.show}>
|
||||||
|
<i class="ri-more-line" />
|
||||||
|
</div>
|
||||||
|
<DropdownMenu bind:this={dropdown} {anchor} align="left">
|
||||||
|
{#if editing}
|
||||||
|
<h5>Edit Table</h5>
|
||||||
|
<div class="container">
|
||||||
|
<Input placeholder="Table Name" thin bind:value={table.name} />
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="button-margin-3">
|
||||||
|
<Button secondary on:click={hideEditor}>Cancel</Button>
|
||||||
|
</div>
|
||||||
|
<div class="button-margin-4">
|
||||||
|
<Button primary on:click={save}>Save</Button>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
{:else}
|
||||||
|
<ul>
|
||||||
|
<li on:click={showEditor}>
|
||||||
|
<Icon name="edit" />
|
||||||
|
Edit
|
||||||
|
</li>
|
||||||
|
<li data-cy="delete-table" on:click={deleteTable}>
|
||||||
|
<Icon name="delete" />
|
||||||
|
Delete
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h5 {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: var(--spacing-xl) 0 0 var(--spacing-xl);
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: var(--spacing-s) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: var(--spacing-s) var(--spacing-m);
|
||||||
|
margin: auto 0px;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover {
|
||||||
|
background-color: var(--grey-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li:active {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 20px;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||||
|
gap: 20px;
|
||||||
|
background: var(--grey-1);
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-1 {
|
||||||
|
grid-column-start: 1;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-3 {
|
||||||
|
grid-column-start: 3;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-margin-4 {
|
||||||
|
grid-column-start: 4;
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,107 +0,0 @@
|
||||||
<script>
|
|
||||||
import { backendUiStore } from "builderStore"
|
|
||||||
import { uuid } from "builderStore/uuid"
|
|
||||||
import { fade } from "svelte/transition"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import { FIELDS, BLOCKS, MODELS } from "constants/backend"
|
|
||||||
import Block from "components/common/Block.svelte"
|
|
||||||
|
|
||||||
function addNewField(field) {
|
|
||||||
backendUiStore.actions.models.addField(field)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createModel(model) {
|
|
||||||
const { schema, ...rest } = $backendUiStore.selectedModel
|
|
||||||
|
|
||||||
backendUiStore.actions.models.save({
|
|
||||||
model: {
|
|
||||||
...model,
|
|
||||||
...rest,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
notifier.success(`${model.name} table created.`)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<section transition:fade>
|
|
||||||
<header>
|
|
||||||
<h2>Create New Table</h2>
|
|
||||||
<p>Before you can view your table, you need to set it up.</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="block-row">
|
|
||||||
<span class="block-row-title">Fields</span>
|
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
|
||||||
<div class="blocks">
|
|
||||||
{#each Object.values(FIELDS) as field}
|
|
||||||
<Block
|
|
||||||
primary
|
|
||||||
title={field.name}
|
|
||||||
icon={field.icon}
|
|
||||||
on:click={() => addNewField(field)} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block-row">
|
|
||||||
<span class="block-row-title">Blocks</span>
|
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
|
||||||
<div class="blocks">
|
|
||||||
{#each Object.values(BLOCKS) as field}
|
|
||||||
<Block
|
|
||||||
secondary
|
|
||||||
title={field.name}
|
|
||||||
icon={field.icon}
|
|
||||||
on:click={() => addNewField(field)} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="block-row">
|
|
||||||
<span class="block-row-title">Tables</span>
|
|
||||||
<p>Blocks are pre-made fields and help you build your table quicker.</p>
|
|
||||||
<div class="blocks">
|
|
||||||
{#each Object.values(MODELS) as model}
|
|
||||||
<Block
|
|
||||||
tertiary
|
|
||||||
title={model.name}
|
|
||||||
icon={model.icon}
|
|
||||||
on:click={() => createModel(model)} />
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
section {
|
|
||||||
height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-row-title {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin-top: 8px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-row {
|
|
||||||
margin-top: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.block-row .blocks {
|
|
||||||
display: grid;
|
|
||||||
grid-auto-flow: column;
|
|
||||||
grid-auto-columns: 110px;
|
|
||||||
grid-gap: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -9,6 +9,7 @@
|
||||||
<div class:selected on:click class={className}>
|
<div class:selected on:click class={className}>
|
||||||
<i class:indented class={icon} />
|
<i class:indented class={icon} />
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -20,7 +21,8 @@
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: 30px 1fr 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transition: 0.3s background-color;
|
transition: 0.3s background-color;
|
||||||
color: var(--ink);
|
color: var(--ink);
|
||||||
|
|
|
@ -4,23 +4,13 @@
|
||||||
import { Switcher } from "@budibase/bbui"
|
import { Switcher } from "@budibase/bbui"
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { store, backendUiStore } from "builderStore"
|
import { store, backendUiStore } from "builderStore"
|
||||||
import BlockNavigator from "./BlockNavigator.svelte"
|
|
||||||
import ListItem from "./ListItem.svelte"
|
import ListItem from "./ListItem.svelte"
|
||||||
import { Button } from "@budibase/bbui"
|
import { Button } from "@budibase/bbui"
|
||||||
|
import CreateTablePopover from "./CreateTable.svelte"
|
||||||
|
import EditTablePopover from "./EditTable.svelte"
|
||||||
|
|
||||||
const { open, close } = getContext("simple-modal")
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
let HEADINGS = [
|
|
||||||
{
|
|
||||||
title: "Navigate",
|
|
||||||
key: "NAVIGATE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Add",
|
|
||||||
key: "ADD",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
$: selectedTab = $backendUiStore.tabs.NAVIGATION_PANEL
|
$: selectedTab = $backendUiStore.tabs.NAVIGATION_PANEL
|
||||||
|
|
||||||
function selectModel(model, fieldId) {
|
function selectModel(model, fieldId) {
|
||||||
|
@ -33,59 +23,35 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupForNewModel() {
|
|
||||||
backendUiStore.update(state => {
|
|
||||||
state.selectedModel = {}
|
|
||||||
state.draftModel = { schema: {} }
|
|
||||||
state.tabs.SETUP_PANEL = "SETUP"
|
|
||||||
return state
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="items-root">
|
<div class="items-root">
|
||||||
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
|
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
|
||||||
<div class="hierarchy">
|
<div class="hierarchy">
|
||||||
<div class="components-list-container">
|
<div class="components-list-container">
|
||||||
<Switcher
|
<h4>Tables</h4>
|
||||||
headings={HEADINGS}
|
<CreateTablePopover />
|
||||||
bind:value={$backendUiStore.tabs.NAVIGATION_PANEL}>
|
|
||||||
{#if selectedTab === 'NAVIGATE'}
|
|
||||||
<Button purple wide on:click={setupForNewModel}>
|
|
||||||
Create New Table
|
|
||||||
</Button>
|
|
||||||
<div class="hierarchy-items-container">
|
<div class="hierarchy-items-container">
|
||||||
{#each $backendUiStore.models as model}
|
{#each $backendUiStore.models as model}
|
||||||
<ListItem
|
<ListItem
|
||||||
selected={!$backendUiStore.selectedField && model._id === $backendUiStore.selectedModel._id}
|
selected={!$backendUiStore.selectedField && model._id === $backendUiStore.selectedModel._id}
|
||||||
title={model.name}
|
title={model.name}
|
||||||
icon="ri-table-fill"
|
icon="ri-table-fill"
|
||||||
on:click={() => selectModel(model)} />
|
on:click={() => selectModel(model)}>
|
||||||
{#if model._id === $backendUiStore.selectedModel._id}
|
<EditTablePopover table={model} />
|
||||||
<div in:slide>
|
</ListItem>
|
||||||
{#each Object.keys(model.schema) as fieldName}
|
|
||||||
<ListItem
|
|
||||||
selected={model._id === $backendUiStore.selectedModel._id && fieldName === $backendUiStore.selectedField}
|
|
||||||
indented
|
|
||||||
icon="ri-layout-column-line"
|
|
||||||
title={model.schema[fieldName].name}
|
|
||||||
on:click={() => selectModel(model, fieldName)} />
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{:else if selectedTab === 'ADD'}
|
|
||||||
<BlockNavigator />
|
|
||||||
{/if}
|
|
||||||
</Switcher>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
h4 {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
.items-root {
|
.items-root {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
if (field) {
|
if (field) {
|
||||||
const name = model.schema[field].name
|
const name = model.schema[field].name
|
||||||
delete model.schema[field]
|
delete model.schema[field]
|
||||||
backendUiStore.actions.models.save({ model })
|
backendUiStore.actions.models.save(model)
|
||||||
notifier.danger(`Field ${name} deleted.`)
|
notifier.danger(`Field ${name} deleted.`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -81,9 +81,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await backendUiStore.actions.models.save({
|
await backendUiStore.actions.models.save($backendUiStore.draftModel)
|
||||||
model: $backendUiStore.draftModel,
|
|
||||||
})
|
|
||||||
notifier.success(
|
notifier.success(
|
||||||
"Success! Your changes have been saved. Please continue on with your greatness."
|
"Success! Your changes have been saved. Please continue on with your greatness."
|
||||||
)
|
)
|
||||||
|
@ -99,6 +97,7 @@
|
||||||
<div class="titled-input">
|
<div class="titled-input">
|
||||||
<header>Name</header>
|
<header>Name</header>
|
||||||
<input
|
<input
|
||||||
|
data-cy="table-name-input"
|
||||||
type="text"
|
type="text"
|
||||||
class="budibase__input"
|
class="budibase__input"
|
||||||
bind:value={$backendUiStore.draftModel.name} />
|
bind:value={$backendUiStore.draftModel.name} />
|
||||||
|
@ -130,7 +129,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
width: 100%;
|
width: 260px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<span class="topnavitemright" on:click={showSettingsModal}>
|
<span class="topnavitemright settings" on:click={showSettingsModal}>
|
||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -14,14 +14,6 @@
|
||||||
|
|
||||||
const getProperties = name => panelDefinition[name]
|
const getProperties = name => panelDefinition[name]
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
// if(propGroup) {
|
|
||||||
// propGroup.addEventListener("scroll", function(e){
|
|
||||||
// console.log("I SCROLLED", e.target.scrollTop)
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
|
|
||||||
function onChange(category) {
|
function onChange(category) {
|
||||||
selectedCategory = category
|
selectedCategory = category
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
<div class="property-control">
|
<div class="property-control">
|
||||||
<div class="label">{label}</div>
|
<div class="label">{label}</div>
|
||||||
<div class="control">
|
<div data-cy={`${key}-prop-control`} class="control">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={control}
|
this={control}
|
||||||
{...handlevalueKey(value)}
|
{...handlevalueKey(value)}
|
||||||
|
|
|
@ -306,7 +306,7 @@ export default {
|
||||||
label: "destinationUrl",
|
label: "destinationUrl",
|
||||||
key: "destinationUrl",
|
key: "destinationUrl",
|
||||||
control: Input,
|
control: Input,
|
||||||
placeholder: "/table/_id",
|
placeholder: "/table/{{context._id}}",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -429,38 +429,6 @@ export default {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Data Chart",
|
|
||||||
_component: "@budibase/standard-components/datachart",
|
|
||||||
description: "Shiny chart",
|
|
||||||
icon: "ri-bar-chart-line",
|
|
||||||
properties: {
|
|
||||||
design: { ...all },
|
|
||||||
settings: [
|
|
||||||
{ label: "Table", key: "model", control: ModelSelect },
|
|
||||||
{
|
|
||||||
label: "Chart Type",
|
|
||||||
key: "type",
|
|
||||||
control: OptionSelect,
|
|
||||||
options: [
|
|
||||||
"column2d",
|
|
||||||
"column3d",
|
|
||||||
"line",
|
|
||||||
"area2d",
|
|
||||||
"bar2d",
|
|
||||||
"bar3d",
|
|
||||||
"pie2d",
|
|
||||||
"pie3d",
|
|
||||||
"doughnut2d",
|
|
||||||
"doughnut3d",
|
|
||||||
"pareto2d",
|
|
||||||
"pareto3d",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "Chart",
|
name: "Chart",
|
||||||
description: "Shiny chart",
|
description: "Shiny chart",
|
||||||
|
@ -471,26 +439,7 @@ export default {
|
||||||
_component: "@budibase/standard-components/donut",
|
_component: "@budibase/standard-components/donut",
|
||||||
description: "Donut chart",
|
description: "Donut chart",
|
||||||
icon: "ri-donut-chart-line",
|
icon: "ri-donut-chart-line",
|
||||||
presetProps: {
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
quantity: 1,
|
|
||||||
percentage: 50,
|
|
||||||
name: "glittering",
|
|
||||||
id: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
quantity: 1,
|
|
||||||
percentage: 50,
|
|
||||||
name: "luminous",
|
|
||||||
id: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
properties: {
|
properties: {
|
||||||
design: {
|
|
||||||
...all,
|
|
||||||
},
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Table",
|
label: "Table",
|
||||||
|
@ -504,14 +453,14 @@ export default {
|
||||||
control: Checkbox,
|
control: Checkbox,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Keep Last Hover",
|
label: "Hover Highlight",
|
||||||
key: "hasLastHoverSliceHighlighted",
|
key: "hasHoverAnimation",
|
||||||
valueKey: "checked",
|
valueKey: "checked",
|
||||||
control: Checkbox,
|
control: Checkbox,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Hover Highlight",
|
label: "Keep Last Hover",
|
||||||
key: "hasHoverAnimation",
|
key: "hasLastHoverSliceHighlighted",
|
||||||
valueKey: "checked",
|
valueKey: "checked",
|
||||||
control: Checkbox,
|
control: Checkbox,
|
||||||
},
|
},
|
||||||
|
@ -532,6 +481,16 @@ export default {
|
||||||
"yellow",
|
"yellow",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Name Field",
|
||||||
|
key: "nameKey",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Value Field",
|
||||||
|
key: "valueKey",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "External Radius",
|
label: "External Radius",
|
||||||
key: "externalRadius",
|
key: "externalRadius",
|
||||||
|
@ -564,12 +523,8 @@ export default {
|
||||||
key: "legendWidth",
|
key: "legendWidth",
|
||||||
control: Input,
|
control: Input,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "Legend Height",
|
|
||||||
key: "legendHeight",
|
|
||||||
control: Input,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1290,6 +1245,15 @@ export default {
|
||||||
_component: "@budibase/standard-components/groupedbar",
|
_component: "@budibase/standard-components/groupedbar",
|
||||||
description: "Groupedbar chart",
|
description: "Groupedbar chart",
|
||||||
icon: "ri-bar-chart-fill",
|
icon: "ri-bar-chart-fill",
|
||||||
|
presetProps: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
name: "2011-01",
|
||||||
|
group: "Direct",
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
|
@ -1329,6 +1293,11 @@ export default {
|
||||||
key: "aspectRatio",
|
key: "aspectRatio",
|
||||||
control: Input,
|
control: Input,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Height",
|
||||||
|
key: "height",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Grid",
|
label: "Grid",
|
||||||
key: "grid",
|
key: "grid",
|
||||||
|
@ -1345,11 +1314,6 @@ export default {
|
||||||
key: "nameLabel",
|
key: "nameLabel",
|
||||||
control: Input,
|
control: Input,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "Value Label",
|
|
||||||
key: "valueLabel",
|
|
||||||
control: Input,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: "Y Ticks",
|
label: "Y Ticks",
|
||||||
key: "yTicks",
|
key: "yTicks",
|
||||||
|
@ -1580,6 +1544,58 @@ export default {
|
||||||
_component: "@budibase/standard-components/line",
|
_component: "@budibase/standard-components/line",
|
||||||
description: "Line chart",
|
description: "Line chart",
|
||||||
icon: "ri-bar-chart-fill",
|
icon: "ri-bar-chart-fill",
|
||||||
|
presetProps: {
|
||||||
|
data: {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
topicName: "San Francisco",
|
||||||
|
name: 1,
|
||||||
|
date: "2020-01-16",
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "San Fran",
|
||||||
|
name: 2,
|
||||||
|
date: "2020-01-17",
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "LA",
|
||||||
|
name: 3,
|
||||||
|
date: "2020-01-18",
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "Toronto",
|
||||||
|
name: 4,
|
||||||
|
date: "2020-01-19",
|
||||||
|
value: 7,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "Van",
|
||||||
|
name: 4,
|
||||||
|
date: "2020-01-20",
|
||||||
|
value: 12,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "Dundee",
|
||||||
|
name: 4,
|
||||||
|
date: "2020-01-21",
|
||||||
|
value: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
topicName: "Dublin",
|
||||||
|
name: 4,
|
||||||
|
date: "2020-01-22",
|
||||||
|
value: 31,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
aspectRatio: 0.5,
|
||||||
|
grid: "horizontal",
|
||||||
|
dateLabel: "fullDate",
|
||||||
|
shouldShowAllDataPoints: true,
|
||||||
|
},
|
||||||
properties: {
|
properties: {
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
|
@ -1587,6 +1603,16 @@ export default {
|
||||||
key: "model",
|
key: "model",
|
||||||
control: ModelSelect,
|
control: ModelSelect,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "X Axis Combo",
|
||||||
|
key: "axisTimeCombinations",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "X Axis Combo",
|
||||||
|
key: "axisTimeCombinations",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Colors",
|
label: "Colors",
|
||||||
key: "color",
|
key: "color",
|
||||||
|
@ -1626,16 +1652,6 @@ export default {
|
||||||
key: "dateLabel",
|
key: "dateLabel",
|
||||||
control: Input,
|
control: Input,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: "Topic Label",
|
|
||||||
key: "topicLabel",
|
|
||||||
control: Input,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Value Label",
|
|
||||||
key: "valueLabel",
|
|
||||||
control: Input,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: "Width",
|
label: "Width",
|
||||||
key: "width",
|
key: "width",
|
||||||
|
@ -1669,6 +1685,21 @@ export default {
|
||||||
"catmullRom",
|
"catmullRom",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Locale",
|
||||||
|
key: "locale",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Topic Label",
|
||||||
|
key: "topicLabel",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Value Label",
|
||||||
|
key: "valueLabel",
|
||||||
|
control: Input,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "X Axis Label",
|
label: "X Axis Label",
|
||||||
key: "xAxisLabel",
|
key: "xAxisLabel",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export const FIELDS = {
|
export const FIELDS = {
|
||||||
PLAIN_TEXT: {
|
STRING: {
|
||||||
name: "Plain Text",
|
name: "Plain Text",
|
||||||
icon: "ri-text",
|
icon: "ri-text",
|
||||||
type: "string",
|
type: "string",
|
||||||
|
@ -65,15 +65,15 @@ export const FIELDS = {
|
||||||
// presence: { allowEmpty: true },
|
// presence: { allowEmpty: true },
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
LINKED_FIELDS: {
|
// LINKED_FIELDS: {
|
||||||
name: "Linked Fields",
|
// name: "Linked Fields",
|
||||||
icon: "ri-link",
|
// icon: "ri-link",
|
||||||
type: "link",
|
// type: "link",
|
||||||
modelId: null,
|
// modelId: null,
|
||||||
constraints: {
|
// constraints: {
|
||||||
type: "array",
|
// type: "array",
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BLOCKS = {
|
export const BLOCKS = {
|
||||||
|
@ -195,36 +195,3 @@ export const BLOCKS = {
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MODELS = {
|
|
||||||
CONTACTS: {
|
|
||||||
icon: "ri-contacts-book-line",
|
|
||||||
name: "Contacts",
|
|
||||||
schema: {
|
|
||||||
Name: BLOCKS.NAME,
|
|
||||||
"Phone Number": BLOCKS.PHONE_NUMBER,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RECIPES: {
|
|
||||||
icon: "ri-link",
|
|
||||||
name: "Recipes",
|
|
||||||
schema: {
|
|
||||||
Name: BLOCKS.NAME,
|
|
||||||
Cuisine: {
|
|
||||||
...FIELDS.PLAIN_TEXT,
|
|
||||||
name: "Cuisine",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
SPORTS_TEAM: {
|
|
||||||
icon: "ri-basketball-line",
|
|
||||||
name: "Sports Team",
|
|
||||||
schema: {
|
|
||||||
Name: BLOCKS.NAME,
|
|
||||||
Championships: {
|
|
||||||
...FIELDS.NUMBER,
|
|
||||||
name: "Championships",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,16 +14,13 @@
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
<div class="nav">
|
|
||||||
<ModelSetupNav />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.root {
|
.root {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 300px minmax(0, 1fr) 300px;
|
grid-template-columns: 300px minmax(0, 1fr);
|
||||||
background: var(--grey-1);
|
background: var(--grey-1);
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { Button } from "@budibase/bbui"
|
import { Button } from "@budibase/bbui"
|
||||||
import EmptyModel from "components/nav/ModelNavigator/EmptyModel.svelte"
|
|
||||||
import ModelDataTable from "components/database/ModelDataTable"
|
import ModelDataTable from "components/database/ModelDataTable"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import ActionButton from "components/common/ActionButton.svelte"
|
import ActionButton from "components/common/ActionButton.svelte"
|
||||||
|
@ -11,16 +10,6 @@
|
||||||
const { open, close } = getContext("simple-modal")
|
const { open, close } = getContext("simple-modal")
|
||||||
|
|
||||||
$: selectedModel = $backendUiStore.selectedModel
|
$: selectedModel = $backendUiStore.selectedModel
|
||||||
|
|
||||||
const createNewRecord = () => {
|
|
||||||
open(
|
|
||||||
CreateEditRecordModal,
|
|
||||||
{
|
|
||||||
onClosed: close,
|
|
||||||
},
|
|
||||||
{ styleContent: { padding: "0" } }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $backendUiStore.selectedDatabase._id && selectedModel.name}
|
{#if $backendUiStore.selectedDatabase._id && selectedModel.name}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "budibase",
|
"name": "budibase",
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"description": "Budibase CLI",
|
"description": "Budibase CLI",
|
||||||
"repository": "https://github.com/Budibase/Budibase",
|
"repository": "https://github.com/Budibase/Budibase",
|
||||||
"homepage": "https://www.budibase.com",
|
"homepage": "https://www.budibase.com",
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/server": "^0.1.13",
|
"@budibase/server": "^0.1.17",
|
||||||
"@inquirer/password": "^0.0.6-alpha.0",
|
"@inquirer/password": "^0.0.6-alpha.0",
|
||||||
"chalk": "^2.4.2",
|
"chalk": "^2.4.2",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
|
@ -29,5 +29,5 @@
|
||||||
"uuid": "^7.0.3",
|
"uuid": "^7.0.3",
|
||||||
"yargs": "^14.2.0"
|
"yargs": "^14.2.0"
|
||||||
},
|
},
|
||||||
"gitHead": "eff4fa93ca1db11b97b5fdedc0c488413e277eb8"
|
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ module.exports = async ({ dir }) => {
|
||||||
|
|
||||||
// dont make this a variable or top level require
|
// dont make this a variable or top level require
|
||||||
// it will cause environment module to be loaded prematurely
|
// it will cause environment module to be loaded prematurely
|
||||||
return require("@budibase/server/src/app")().then(server => {
|
const server = require("@budibase/server/src/app")
|
||||||
server.on("close", () => console.log("Server Closed"))
|
server.on("close", () => console.log("Server Closed"))
|
||||||
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.1.1",
|
"version": "0.1.17",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
"module": "dist/budibase-client.esm.mjs",
|
"module": "dist/budibase-client.esm.mjs",
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/client": "^0.1.1",
|
"@budibase/client": "^0.1.17",
|
||||||
"@budibase/standard-components": "^0.1.13",
|
"@budibase/standard-components": "^0.1.17",
|
||||||
"@material/button": "^4.0.0",
|
"@material/button": "^4.0.0",
|
||||||
"@material/checkbox": "^4.0.0",
|
"@material/checkbox": "^4.0.0",
|
||||||
"@material/data-table": "4.0.0",
|
"@material/data-table": "4.0.0",
|
||||||
|
@ -50,9 +50,9 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "eff4fa93ca1db11b97b5fdedc0c488413e277eb8",
|
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/card": "4.0.0"
|
"@material/card": "4.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/electron.js",
|
"main": "src/electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
"author": "Michael Shanks",
|
"author": "Michael Shanks",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/client": "^0.1.1",
|
"@budibase/client": "^0.1.17",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sendgrid/mail": "^7.1.1",
|
"@sendgrid/mail": "^7.1.1",
|
||||||
"@sentry/node": "^5.19.2",
|
"@sentry/node": "^5.19.2",
|
||||||
|
@ -97,5 +97,5 @@
|
||||||
"./scripts/jestSetup.js"
|
"./scripts/jestSetup.js"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "eff4fa93ca1db11b97b5fdedc0c488413e277eb8"
|
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691"
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,7 +207,7 @@ const getClientId = ctx => {
|
||||||
env.CLIENT_ID
|
env.CLIENT_ID
|
||||||
|
|
||||||
if (!clientId) {
|
if (!clientId) {
|
||||||
ctx.throw(400, "ClientId not suplied")
|
ctx.throw(400, "ClientId not supplied")
|
||||||
}
|
}
|
||||||
return clientId
|
return clientId
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,9 @@ exports.save = async function(ctx) {
|
||||||
|
|
||||||
ctx.eventEmitter &&
|
ctx.eventEmitter &&
|
||||||
ctx.eventEmitter.emit(`record:save`, {
|
ctx.eventEmitter.emit(`record:save`, {
|
||||||
|
args: {
|
||||||
record,
|
record,
|
||||||
|
},
|
||||||
instanceId: ctx.user.instanceId,
|
instanceId: ctx.user.instanceId,
|
||||||
})
|
})
|
||||||
ctx.body = record
|
ctx.body = record
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
const recordController = require("../../record")
|
const recordController = require("../../record")
|
||||||
|
|
||||||
module.exports = async function saveRecord({ args, instanceId }) {
|
module.exports = async function saveRecord({ args, context }) {
|
||||||
const { model, ...record } = args.record
|
const { model, ...record } = args.record
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
params: {
|
params: {
|
||||||
instanceId,
|
instanceId: context.instanceId,
|
||||||
modelId: model._id,
|
modelId: model._id,
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
body: record,
|
body: record,
|
||||||
},
|
},
|
||||||
user: { instanceId },
|
user: { instanceId: context.instanceId },
|
||||||
}
|
}
|
||||||
|
|
||||||
await recordController.save(ctx)
|
await recordController.save(ctx)
|
||||||
|
|
|
@ -44,7 +44,9 @@ router
|
||||||
useAppRootPath: true,
|
useAppRootPath: true,
|
||||||
}
|
}
|
||||||
ctx.isDev =
|
ctx.isDev =
|
||||||
process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "jest"
|
process.env.NODE_ENV !== "production" &&
|
||||||
|
process.env.NODE_ENV !== "jest" &&
|
||||||
|
process.env.NODE_ENV !== "cypress"
|
||||||
await next()
|
await next()
|
||||||
})
|
})
|
||||||
.use(authenticated)
|
.use(authenticated)
|
||||||
|
|
|
@ -18,7 +18,7 @@ async function executeRelevantWorkflows(event, eventType) {
|
||||||
workflowOrchestrator.strategy = serverStrategy
|
workflowOrchestrator.strategy = serverStrategy
|
||||||
|
|
||||||
for (let workflow of workflows) {
|
for (let workflow of workflows) {
|
||||||
workflowOrchestrator.execute(workflow)
|
workflowOrchestrator.execute(workflow, event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ exports.Orchestrator = class Orchestrator {
|
||||||
this._strategy = strategy()
|
this._strategy = strategy()
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(workflow) {
|
async execute(workflow, context) {
|
||||||
if (workflow.live) {
|
if (workflow.live) {
|
||||||
this._strategy.run(workflow.definition)
|
this._strategy.run(workflow.definition, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,12 +35,15 @@ exports.serverStrategy = () => ({
|
||||||
|
|
||||||
return mappedArgs
|
return mappedArgs
|
||||||
},
|
},
|
||||||
run: async function(workflow) {
|
run: async function(workflow, context) {
|
||||||
for (let block of workflow.steps) {
|
for (let block of workflow.steps) {
|
||||||
if (block.type === "CLIENT") continue
|
if (block.type === "CLIENT") continue
|
||||||
|
|
||||||
const action = require(`../api/controllers/workflow/actions/${block.actionId}`)
|
const action = require(`../api/controllers/workflow/actions/${block.actionId}`)
|
||||||
const response = await action({ args: this.bindContextArgs(block.args) })
|
const response = await action({
|
||||||
|
args: this.bindContextArgs(block.args),
|
||||||
|
context,
|
||||||
|
})
|
||||||
|
|
||||||
this.context = {
|
this.context = {
|
||||||
...this.context,
|
...this.context,
|
||||||
|
|
|
@ -18,7 +18,6 @@ exports.downloadExtractComponentLibraries = async appFolder => {
|
||||||
// Need to download tarballs directly from NPM as our users may not have node on their machine
|
// Need to download tarballs directly from NPM as our users may not have node on their machine
|
||||||
for (let lib of LIBRARIES) {
|
for (let lib of LIBRARIES) {
|
||||||
// download tarball
|
// download tarball
|
||||||
// TODO: make sure the latest version is always downloaded
|
|
||||||
const registryUrl = `https://registry.npmjs.org/@budibase/${lib}/-/${lib}-${packageJson.version}.tgz`
|
const registryUrl = `https://registry.npmjs.org/@budibase/${lib}/-/${lib}-${packageJson.version}.tgz`
|
||||||
const response = await fetch(registryUrl)
|
const response = await fetch(registryUrl)
|
||||||
if (!response.ok)
|
if (!response.ok)
|
||||||
|
|
|
@ -287,13 +287,14 @@
|
||||||
"hasLastHoverSliceHighlighted": "bool",
|
"hasLastHoverSliceHighlighted": "bool",
|
||||||
"hasHoverAnimation": "bool",
|
"hasHoverAnimation": "bool",
|
||||||
"numberFormat": "string",
|
"numberFormat": "string",
|
||||||
|
"nameKey": "string",
|
||||||
|
"valueKey": "string",
|
||||||
"isAnimated": "bool",
|
"isAnimated": "bool",
|
||||||
"externalRadius": "number",
|
"externalRadius": "number",
|
||||||
"internalRadius": "number",
|
"internalRadius": "number",
|
||||||
"radiusHoverOffset": "number",
|
"radiusHoverOffset": "number",
|
||||||
"percentageFormat": "string",
|
"percentageFormat": "string",
|
||||||
"useLegend": "bool",
|
"useLegend": "bool",
|
||||||
"horizontalLegend": "bool",
|
|
||||||
"legendWidth": "number",
|
"legendWidth": "number",
|
||||||
"legendHeight": "number"
|
"legendHeight": "number"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/client": "^0.1.1",
|
"@budibase/client": "^0.1.17",
|
||||||
"@nx-js/compiler-util": "^2.0.0",
|
"@nx-js/compiler-util": "^2.0.0",
|
||||||
"@rollup/plugin-commonjs": "^11.1.0",
|
"@rollup/plugin-commonjs": "^11.1.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
|
@ -33,9 +33,9 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.1.13",
|
"version": "0.1.17",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "eff4fa93ca1db11b97b5fdedc0c488413e277eb8",
|
"gitHead": "284cceb9b703c38566c6e6363c022f79a08d5691",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@beyonk/svelte-googlemaps": "^2.2.0",
|
"@beyonk/svelte-googlemaps": "^2.2.0",
|
||||||
"britecharts": "^2.16.0",
|
"britecharts": "^2.16.0",
|
||||||
|
|
|
@ -10,4 +10,3 @@ export { default as scatterplot } from "./ScatterPlot.svelte"
|
||||||
export { default as step } from "./Step.svelte"
|
export { default as step } from "./Step.svelte"
|
||||||
export { default as stackedarea } from "./StackedArea.svelte"
|
export { default as stackedarea } from "./StackedArea.svelte"
|
||||||
export { default as stackedbar } from "./StackedBar.svelte"
|
export { default as stackedbar } from "./StackedBar.svelte"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue